Merge "Revert "Assembly TLAB and RegionTLAB allocation fast path for x86""
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 8309bd8..e9b6a6d 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -269,12 +269,14 @@
compiler/optimizing/nodes_test.cc \
compiler/optimizing/parallel_move_test.cc \
compiler/optimizing/pretty_printer_test.cc \
+ compiler/optimizing/reference_type_propagation_test.cc \
compiler/optimizing/side_effects_test.cc \
compiler/optimizing/ssa_test.cc \
compiler/optimizing/stack_map_test.cc \
compiler/optimizing/suspend_check_test.cc \
compiler/utils/arena_allocator_test.cc \
compiler/utils/dedupe_set_test.cc \
+ compiler/utils/intrusive_forward_list_test.cc \
compiler/utils/swap_space_test.cc \
compiler/utils/test_dex_file_builder_test.cc \
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index b4ecbd8..7277107 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -210,7 +210,14 @@
} else {
if ((insn & 0xfffffc00) == 0x91000000) {
// ADD immediate, 64-bit with imm12 == 0 (unset).
- DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative) << patch.GetType();
+ if (!kEmitCompilerReadBarrier) {
+ DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative) << patch.GetType();
+ } else {
+ // With the read barrier (non-baker) enabled, it could be kDexCacheArray in the
+ // HLoadString::LoadKind::kDexCachePcRelative case of VisitLoadString().
+ DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative ||
+ patch.GetType() == LinkerPatch::Type::kDexCacheArray) << patch.GetType();
+ }
shift = 0u; // No shift for ADD.
} else {
// LDR 32-bit or 64-bit with imm12 == 0 (unset).
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 084360f..659c6f8 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -1206,9 +1206,9 @@
GetGraph()->GetArena()->Adapter(kArenaAllocBoundsCheckElimination));
ArenaVector<HBoundsCheck*> standby(
GetGraph()->GetArena()->Adapter(kArenaAllocBoundsCheckElimination));
- for (HUseIterator<HInstruction*> it2(array_length->GetUses()); !it2.Done(); it2.Advance()) {
+ for (const HUseListNode<HInstruction*>& use : array_length->GetUses()) {
// Another bounds check in same or dominated block?
- HInstruction* user = it2.Current()->GetUser();
+ HInstruction* user = use.GetUser();
HBasicBlock* other_block = user->GetBlock();
if (user->IsBoundsCheck() && block->Dominates(other_block)) {
HBoundsCheck* other_bounds_check = user->AsBoundsCheck();
@@ -1635,29 +1635,33 @@
Primitive::Type type = instruction->GetType();
HPhi* phi = nullptr;
// Scan all uses of an instruction and replace each later use with a phi node.
- for (HUseIterator<HInstruction*> it2(instruction->GetUses());
- !it2.Done();
- it2.Advance()) {
- HInstruction* user = it2.Current()->GetUser();
+ const HUseList<HInstruction*>& uses = instruction->GetUses();
+ for (auto it2 = uses.begin(), end2 = uses.end(); it2 != end2; /* ++it2 below */) {
+ HInstruction* user = it2->GetUser();
+ size_t index = it2->GetIndex();
+ // Increment `it2` now because `*it2` may disappear thanks to user->ReplaceInput().
+ ++it2;
if (user->GetBlock() != true_block) {
if (phi == nullptr) {
phi = NewPhi(new_preheader, instruction, type);
}
- user->ReplaceInput(phi, it2.Current()->GetIndex());
+ user->ReplaceInput(phi, index); // Removes the use node from the list.
}
}
// Scan all environment uses of an instruction and replace each later use with a phi node.
- for (HUseIterator<HEnvironment*> it2(instruction->GetEnvUses());
- !it2.Done();
- it2.Advance()) {
- HEnvironment* user = it2.Current()->GetUser();
+ const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
+ for (auto it2 = env_uses.begin(), end2 = env_uses.end(); it2 != end2; /* ++it2 below */) {
+ HEnvironment* user = it2->GetUser();
+ size_t index = it2->GetIndex();
+ // Increment `it2` now because `*it2` may disappear thanks to user->RemoveAsUserOfInput().
+ ++it2;
if (user->GetHolder()->GetBlock() != true_block) {
if (phi == nullptr) {
phi = NewPhi(new_preheader, instruction, type);
}
- user->RemoveAsUserOfInput(it2.Current()->GetIndex());
- user->SetRawEnvAt(it2.Current()->GetIndex(), phi);
- phi->AddEnvUseAt(user, it2.Current()->GetIndex());
+ user->RemoveAsUserOfInput(index);
+ user->SetRawEnvAt(index, phi);
+ phi->AddEnvUseAt(user, index);
}
}
}
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 4f46d5e..580ef72 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -51,7 +51,7 @@
compiler_driver_(driver),
compilation_stats_(compiler_stats),
block_builder_(graph, dex_file, code_item),
- ssa_builder_(graph, handles),
+ ssa_builder_(graph, dex_compilation_unit->GetDexCache(), handles),
instruction_builder_(graph,
&block_builder_,
&ssa_builder_,
@@ -78,7 +78,7 @@
null_dex_cache_(),
compilation_stats_(nullptr),
block_builder_(graph, nullptr, code_item),
- ssa_builder_(graph, handles),
+ ssa_builder_(graph, null_dex_cache_, handles),
instruction_builder_(graph,
&block_builder_,
&ssa_builder_,
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index 6412b24..a849448 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -199,7 +199,7 @@
// For single uses we let VIXL handle the constant generation since it will
// use registers that are not managed by the register allocator (wip0, wip1).
- if (constant->GetUses().HasOnlyOneUse()) {
+ if (constant->GetUses().HasExactlyOneElement()) {
return true;
}
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 9ea4b2d..96837a8 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -342,36 +342,34 @@
// Ensure the uses of `instruction` are defined in a block of the graph,
// and the entry in the use list is consistent.
- for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
- !use_it.Done(); use_it.Advance()) {
- HInstruction* use = use_it.Current()->GetUser();
- const HInstructionList& list = use->IsPhi()
- ? use->GetBlock()->GetPhis()
- : use->GetBlock()->GetInstructions();
- if (!list.Contains(use)) {
+ for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
+ HInstruction* user = use.GetUser();
+ const HInstructionList& list = user->IsPhi()
+ ? user->GetBlock()->GetPhis()
+ : user->GetBlock()->GetInstructions();
+ if (!list.Contains(user)) {
AddError(StringPrintf("User %s:%d of instruction %d is not defined "
"in a basic block of the control-flow graph.",
- use->DebugName(),
- use->GetId(),
+ user->DebugName(),
+ user->GetId(),
instruction->GetId()));
}
- size_t use_index = use_it.Current()->GetIndex();
- if ((use_index >= use->InputCount()) || (use->InputAt(use_index) != instruction)) {
+ size_t use_index = use.GetIndex();
+ if ((use_index >= user->InputCount()) || (user->InputAt(use_index) != instruction)) {
AddError(StringPrintf("User %s:%d of instruction %s:%d has a wrong "
"UseListNode index.",
- use->DebugName(),
- use->GetId(),
+ user->DebugName(),
+ user->GetId(),
instruction->DebugName(),
instruction->GetId()));
}
}
// Ensure the environment uses entries are consistent.
- for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses());
- !use_it.Done(); use_it.Advance()) {
- HEnvironment* use = use_it.Current()->GetUser();
- size_t use_index = use_it.Current()->GetIndex();
- if ((use_index >= use->Size()) || (use->GetInstructionAt(use_index) != instruction)) {
+ for (const HUseListNode<HEnvironment*>& use : instruction->GetEnvUses()) {
+ HEnvironment* user = use.GetUser();
+ size_t use_index = use.GetIndex();
+ if ((use_index >= user->Size()) || (user->GetInstructionAt(use_index) != instruction)) {
AddError(StringPrintf("Environment user of %s:%d has a wrong "
"UseListNode index.",
instruction->DebugName(),
@@ -383,13 +381,11 @@
for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
HUserRecord<HInstruction*> input_record = instruction->InputRecordAt(i);
HInstruction* input = input_record.GetInstruction();
- HUseListNode<HInstruction*>* use_node = input_record.GetUseNode();
- size_t use_index = use_node->GetIndex();
- if ((use_node == nullptr)
- || !input->GetUses().Contains(use_node)
- || (use_index >= e)
- || (use_index != i)) {
- AddError(StringPrintf("Instruction %s:%d has an invalid pointer to use entry "
+ if ((input_record.GetBeforeUseNode() == input->GetUses().end()) ||
+ (input_record.GetUseNode() == input->GetUses().end()) ||
+ !input->GetUses().ContainsNode(*input_record.GetUseNode()) ||
+ (input_record.GetUseNode()->GetIndex() != i)) {
+ AddError(StringPrintf("Instruction %s:%d has an invalid iterator before use entry "
"at input %u (%s:%d).",
instruction->DebugName(),
instruction->GetId(),
@@ -400,18 +396,17 @@
}
// Ensure an instruction dominates all its uses.
- for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
- !use_it.Done(); use_it.Advance()) {
- HInstruction* use = use_it.Current()->GetUser();
- if (!use->IsPhi() && !instruction->StrictlyDominates(use)) {
+ for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
+ HInstruction* user = use.GetUser();
+ if (!user->IsPhi() && !instruction->StrictlyDominates(user)) {
AddError(StringPrintf("Instruction %s:%d in block %d does not dominate "
"use %s:%d in block %d.",
instruction->DebugName(),
instruction->GetId(),
current_block_->GetBlockId(),
- use->DebugName(),
- use->GetId(),
- use->GetBlock()->GetBlockId()));
+ user->DebugName(),
+ user->GetId(),
+ user->GetBlock()->GetBlockId()));
}
}
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index fe47f7d..9efc13f 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -608,12 +608,7 @@
for (HInstructionIterator it(list); !it.Done(); it.Advance()) {
HInstruction* instruction = it.Current();
int bci = 0;
- size_t num_uses = 0;
- for (HUseIterator<HInstruction*> use_it(instruction->GetUses());
- !use_it.Done();
- use_it.Advance()) {
- ++num_uses;
- }
+ size_t num_uses = instruction->GetUses().SizeSlow();
AddIndent();
output_ << bci << " " << num_uses << " "
<< GetTypeId(instruction->GetType()) << instruction->GetId() << " ";
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 77e0cbc..836bcfa 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -411,7 +411,10 @@
// Run type propagation to get the guard typed, and eventually propagate the
// type of the receiver.
- ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false);
+ ReferenceTypePropagation rtp_fixup(graph_,
+ outer_compilation_unit_.GetDexCache(),
+ handles_,
+ /* is_first_run */ false);
rtp_fixup.Run();
MaybeRecordStat(kInlinedMonomorphicCall);
@@ -532,7 +535,10 @@
MaybeRecordStat(kInlinedPolymorphicCall);
// Run type propagation to get the guards typed.
- ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false);
+ ReferenceTypePropagation rtp_fixup(graph_,
+ outer_compilation_unit_.GetDexCache(),
+ handles_,
+ /* is_first_run */ false);
rtp_fixup.Run();
return true;
}
@@ -709,7 +715,10 @@
deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
// Run type propagation to get the guard typed.
- ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false);
+ ReferenceTypePropagation rtp_fixup(graph_,
+ outer_compilation_unit_.GetDexCache(),
+ handles_,
+ /* is_first_run */ false);
rtp_fixup.Run();
MaybeRecordStat(kInlinedPolymorphicCall);
@@ -971,7 +980,8 @@
// dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
/* dex_pc */ 0);
if (iget->GetType() == Primitive::kPrimNot) {
- ReferenceTypePropagation rtp(graph_, handles_, /* is_first_run */ false);
+ // Use the same dex_cache that we used for field lookup as the hint_dex_cache.
+ ReferenceTypePropagation rtp(graph_, dex_cache, handles_, /* is_first_run */ false);
rtp.Visit(iget);
}
return iget;
@@ -1303,11 +1313,12 @@
DCHECK(return_replacement->IsPhi());
size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */, pointer_size);
- if (cls != nullptr) {
+ if (cls != nullptr && !cls->IsErroneous()) {
ReferenceTypeInfo::TypeHandle return_handle = handles_->NewHandle(cls);
return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */));
} else {
+ // Return inexact object type on failures.
return_replacement->SetReferenceTypeInfo(graph_->GetInexactObjectRti());
}
}
@@ -1319,13 +1330,19 @@
if (invoke_rti.IsStrictSupertypeOf(return_rti)
|| (return_rti.IsExact() && !invoke_rti.IsExact())
|| !return_replacement->CanBeNull()) {
- ReferenceTypePropagation(graph_, handles_, /* is_first_run */ false).Run();
+ ReferenceTypePropagation(graph_,
+ outer_compilation_unit_.GetDexCache(),
+ handles_,
+ /* is_first_run */ false).Run();
}
}
} else if (return_replacement->IsInstanceOf()) {
if (do_rtp) {
// Inlining InstanceOf into an If may put a tighter bound on reference types.
- ReferenceTypePropagation(graph_, handles_, /* is_first_run */ false).Run();
+ ReferenceTypePropagation(graph_,
+ outer_compilation_unit_.GetDexCache(),
+ handles_,
+ /* is_first_run */ false).Run();
}
}
}
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 1f66db7..d7b3856 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -409,9 +409,9 @@
return true;
}
- for (HUseIterator<HInstruction*> it(input->GetUses()); !it.Done(); it.Advance()) {
- HInstruction* use = it.Current()->GetUser();
- if (use->IsNullCheck() && use->StrictlyDominates(at)) {
+ for (const HUseListNode<HInstruction*>& use : input->GetUses()) {
+ HInstruction* user = use.GetUser();
+ if (user->IsNullCheck() && user->StrictlyDominates(at)) {
return true;
}
}
@@ -1070,12 +1070,12 @@
}
// Is the Compare only used for this purpose?
- if (!left->GetUses().HasOnlyOneUse()) {
+ if (!left->GetUses().HasExactlyOneElement()) {
// Someone else also wants the result of the compare.
return;
}
- if (!left->GetEnvUses().IsEmpty()) {
+ if (!left->GetEnvUses().empty()) {
// There is a reference to the compare result in an environment. Do we really need it?
if (GetGraph()->IsDebuggable()) {
return;
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index f00d960..e4a711e 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -140,7 +140,7 @@
shift_amount,
use->GetDexPc());
use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op);
- if (bitfield_op->GetUses().IsEmpty()) {
+ if (bitfield_op->GetUses().empty()) {
bitfield_op->GetBlock()->RemoveInstruction(bitfield_op);
}
RecordSimplification();
@@ -160,20 +160,22 @@
const HUseList<HInstruction*>& uses = bitfield_op->GetUses();
// Check whether we can merge the instruction in all its users' shifter operand.
- for (HUseIterator<HInstruction*> it_use(uses); !it_use.Done(); it_use.Advance()) {
- HInstruction* use = it_use.Current()->GetUser();
- if (!HasShifterOperand(use)) {
+ for (const HUseListNode<HInstruction*>& use : uses) {
+ HInstruction* user = use.GetUser();
+ if (!HasShifterOperand(user)) {
return false;
}
- if (!CanMergeIntoShifterOperand(use, bitfield_op)) {
+ if (!CanMergeIntoShifterOperand(user, bitfield_op)) {
return false;
}
}
// Merge the instruction into its uses.
- for (HUseIterator<HInstruction*> it_use(uses); !it_use.Done(); it_use.Advance()) {
- HInstruction* use = it_use.Current()->GetUser();
- bool merged = MergeIntoShifterOperand(use, bitfield_op);
+ for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
+ HInstruction* user = it->GetUser();
+ // Increment `it` now because `*it` will disappear thanks to MergeIntoShifterOperand().
+ ++it;
+ bool merged = MergeIntoShifterOperand(user, bitfield_op);
DCHECK(merged);
}
diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc
index a11b5bd..dab1ebc 100644
--- a/compiler/optimizing/instruction_simplifier_shared.cc
+++ b/compiler/optimizing/instruction_simplifier_shared.cc
@@ -103,13 +103,10 @@
return false;
}
- HInstruction* use = mul->HasNonEnvironmentUses()
- ? mul->GetUses().GetFirst()->GetUser()
- : nullptr;
-
ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena();
if (mul->HasOnlyOneNonEnvironmentUse()) {
+ HInstruction* use = mul->GetUses().front().GetUser();
if (use->IsAdd() || use->IsSub()) {
// Replace code looking like
// MUL tmp, x, y
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index bdaef1d..f9a955f 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -441,7 +441,7 @@
ASSERT_TRUE(range->GetNext() == nullptr);
HPhi* phi = liveness.GetInstructionFromSsaIndex(4)->AsPhi();
- ASSERT_TRUE(phi->GetUses().HasOnlyOneUse());
+ ASSERT_TRUE(phi->GetUses().HasExactlyOneElement());
interval = phi->GetLiveInterval();
range = interval->GetFirstRange();
ASSERT_EQ(26u, range->GetStart());
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index ac7ed86..2de4158 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -43,31 +43,29 @@
// Visit all uses to determine if this reference can spread into the heap,
// a method call, etc.
- for (HUseIterator<HInstruction*> use_it(reference_->GetUses());
- !use_it.Done();
- use_it.Advance()) {
- HInstruction* use = use_it.Current()->GetUser();
- DCHECK(!use->IsNullCheck()) << "NullCheck should have been eliminated";
- if (use->IsBoundType()) {
+ for (const HUseListNode<HInstruction*>& use : reference_->GetUses()) {
+ HInstruction* user = use.GetUser();
+ DCHECK(!user->IsNullCheck()) << "NullCheck should have been eliminated";
+ if (user->IsBoundType()) {
// BoundType shouldn't normally be necessary for a NewInstance.
// Just be conservative for the uncommon cases.
is_singleton_ = false;
is_singleton_and_not_returned_ = false;
return;
}
- if (use->IsPhi() || use->IsSelect() || use->IsInvoke() ||
- (use->IsInstanceFieldSet() && (reference_ == use->InputAt(1))) ||
- (use->IsUnresolvedInstanceFieldSet() && (reference_ == use->InputAt(1))) ||
- (use->IsStaticFieldSet() && (reference_ == use->InputAt(1))) ||
- (use->IsUnresolvedStaticFieldSet() && (reference_ == use->InputAt(0))) ||
- (use->IsArraySet() && (reference_ == use->InputAt(2)))) {
+ if (user->IsPhi() || user->IsSelect() || user->IsInvoke() ||
+ (user->IsInstanceFieldSet() && (reference_ == user->InputAt(1))) ||
+ (user->IsUnresolvedInstanceFieldSet() && (reference_ == user->InputAt(1))) ||
+ (user->IsStaticFieldSet() && (reference_ == user->InputAt(1))) ||
+ (user->IsUnresolvedStaticFieldSet() && (reference_ == user->InputAt(0))) ||
+ (user->IsArraySet() && (reference_ == user->InputAt(2)))) {
// reference_ is merged to HPhi/HSelect, passed to a callee, or stored to heap.
// reference_ isn't the only name that can refer to its value anymore.
is_singleton_ = false;
is_singleton_and_not_returned_ = false;
return;
}
- if (use->IsReturn()) {
+ if (user->IsReturn()) {
is_singleton_and_not_returned_ = false;
}
}
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 1afa36a..fe75451 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -618,7 +618,24 @@
}
}
- if (is_irreducible_loop || graph->IsCompilingOsr()) {
+ if (!is_irreducible_loop && graph->IsCompilingOsr()) {
+ // When compiling in OSR mode, all loops in the compiled method may be entered
+ // from the interpreter. We treat this OSR entry point just like an extra entry
+ // to an irreducible loop, so we need to mark the method's loops as irreducible.
+ // This does not apply to inlined loops which do not act as OSR entry points.
+ if (suspend_check_ == nullptr) {
+ // Just building the graph in OSR mode, this loop is not inlined. We never build an
+ // inner graph in OSR mode as we can do OSR transition only from the outer method.
+ is_irreducible_loop = true;
+ } else {
+ // Look at the suspend check's environment to determine if the loop was inlined.
+ DCHECK(suspend_check_->HasEnvironment());
+ if (!suspend_check_->GetEnvironment()->IsFromInlinedInvoke()) {
+ is_irreducible_loop = true;
+ }
+ }
+ }
+ if (is_irreducible_loop) {
irreducible_ = true;
graph->SetHasIrreducibleLoops(true);
}
@@ -681,8 +698,8 @@
DCHECK_EQ(replacement->GetType(), Primitive::kPrimVoid);
DCHECK_EQ(initial->GetBlock(), this);
DCHECK_EQ(initial->GetType(), Primitive::kPrimVoid);
- DCHECK(initial->GetUses().IsEmpty());
- DCHECK(initial->GetEnvUses().IsEmpty());
+ DCHECK(initial->GetUses().empty());
+ DCHECK(initial->GetEnvUses().empty());
replacement->SetBlock(this);
replacement->SetId(GetGraph()->GetNextInstructionId());
instructions_.InsertInstructionBefore(replacement, initial);
@@ -774,8 +791,8 @@
instruction->SetBlock(nullptr);
instruction_list->RemoveInstruction(instruction);
if (ensure_safety) {
- DCHECK(instruction->GetUses().IsEmpty());
- DCHECK(instruction->GetEnvUses().IsEmpty());
+ DCHECK(instruction->GetUses().empty());
+ DCHECK(instruction->GetEnvUses().empty());
RemoveAsUser(instruction);
}
}
@@ -839,8 +856,11 @@
}
void HEnvironment::RemoveAsUserOfInput(size_t index) const {
- const HUserRecord<HEnvironment*>& user_record = vregs_[index];
- user_record.GetInstruction()->RemoveEnvironmentUser(user_record.GetUseNode());
+ const HUserRecord<HEnvironment*>& env_use = vregs_[index];
+ HInstruction* user = env_use.GetInstruction();
+ auto before_env_use_node = env_use.GetBeforeUseNode();
+ user->env_uses_.erase_after(before_env_use_node);
+ user->FixUpUserRecordsAfterEnvUseRemoval(before_env_use_node);
}
HInstruction::InstructionKind HInstruction::GetKind() const {
@@ -985,30 +1005,36 @@
void HInstruction::ReplaceWith(HInstruction* other) {
DCHECK(other != nullptr);
- for (HUseIterator<HInstruction*> it(GetUses()); !it.Done(); it.Advance()) {
- HUseListNode<HInstruction*>* current = it.Current();
- HInstruction* user = current->GetUser();
- size_t input_index = current->GetIndex();
- user->SetRawInputAt(input_index, other);
- other->AddUseAt(user, input_index);
- }
+ // Note: fixup_end remains valid across splice_after().
+ auto fixup_end = other->uses_.empty() ? other->uses_.begin() : ++other->uses_.begin();
+ other->uses_.splice_after(other->uses_.before_begin(), uses_);
+ other->FixUpUserRecordsAfterUseInsertion(fixup_end);
- for (HUseIterator<HEnvironment*> it(GetEnvUses()); !it.Done(); it.Advance()) {
- HUseListNode<HEnvironment*>* current = it.Current();
- HEnvironment* user = current->GetUser();
- size_t input_index = current->GetIndex();
- user->SetRawEnvAt(input_index, other);
- other->AddEnvUseAt(user, input_index);
- }
+ // Note: env_fixup_end remains valid across splice_after().
+ auto env_fixup_end =
+ other->env_uses_.empty() ? other->env_uses_.begin() : ++other->env_uses_.begin();
+ other->env_uses_.splice_after(other->env_uses_.before_begin(), env_uses_);
+ other->FixUpUserRecordsAfterEnvUseInsertion(env_fixup_end);
- uses_.Clear();
- env_uses_.Clear();
+ DCHECK(uses_.empty());
+ DCHECK(env_uses_.empty());
}
void HInstruction::ReplaceInput(HInstruction* replacement, size_t index) {
- RemoveAsUserOfInput(index);
- SetRawInputAt(index, replacement);
- replacement->AddUseAt(this, index);
+ HUserRecord<HInstruction*> input_use = InputRecordAt(index);
+ if (input_use.GetInstruction() == replacement) {
+ // Nothing to do.
+ return;
+ }
+ HUseList<HInstruction*>::iterator before_use_node = input_use.GetBeforeUseNode();
+ // Note: fixup_end remains valid across splice_after().
+ auto fixup_end =
+ replacement->uses_.empty() ? replacement->uses_.begin() : ++replacement->uses_.begin();
+ replacement->uses_.splice_after(replacement->uses_.before_begin(),
+ input_use.GetInstruction()->uses_,
+ before_use_node);
+ replacement->FixUpUserRecordsAfterUseInsertion(fixup_end);
+ input_use.GetInstruction()->FixUpUserRecordsAfterUseRemoval(before_use_node);
}
size_t HInstruction::EnvironmentSize() const {
@@ -1280,17 +1306,18 @@
DCHECK_EQ(InputCount(), 0u);
// Find the target block.
- HUseIterator<HInstruction*> uses_it(GetUses());
- HBasicBlock* target_block = uses_it.Current()->GetUser()->GetBlock();
- uses_it.Advance();
- while (!uses_it.Done() && uses_it.Current()->GetUser()->GetBlock() == target_block) {
- uses_it.Advance();
+ auto uses_it = GetUses().begin();
+ auto uses_end = GetUses().end();
+ HBasicBlock* target_block = uses_it->GetUser()->GetBlock();
+ ++uses_it;
+ while (uses_it != uses_end && uses_it->GetUser()->GetBlock() == target_block) {
+ ++uses_it;
}
- if (!uses_it.Done()) {
+ if (uses_it != uses_end) {
// This instruction has uses in two or more blocks. Find the common dominator.
CommonDominator finder(target_block);
- for (; !uses_it.Done(); uses_it.Advance()) {
- finder.Update(uses_it.Current()->GetUser()->GetBlock());
+ for (; uses_it != uses_end; ++uses_it) {
+ finder.Update(uses_it->GetUser()->GetBlock());
}
target_block = finder.Get();
DCHECK(target_block != nullptr);
@@ -1303,10 +1330,10 @@
// Find insertion position.
HInstruction* insert_pos = nullptr;
- for (HUseIterator<HInstruction*> uses_it2(GetUses()); !uses_it2.Done(); uses_it2.Advance()) {
- if (uses_it2.Current()->GetUser()->GetBlock() == target_block &&
- (insert_pos == nullptr || uses_it2.Current()->GetUser()->StrictlyDominates(insert_pos))) {
- insert_pos = uses_it2.Current()->GetUser();
+ for (const HUseListNode<HInstruction*>& use : GetUses()) {
+ if (use.GetUser()->GetBlock() == target_block &&
+ (insert_pos == nullptr || use.GetUser()->StrictlyDominates(insert_pos))) {
+ insert_pos = use.GetUser();
}
}
if (insert_pos == nullptr) {
@@ -1586,10 +1613,10 @@
static void RemoveUsesOfDeadInstruction(HInstruction* insn) {
DCHECK(!insn->HasEnvironmentUses());
while (insn->HasNonEnvironmentUses()) {
- HUseListNode<HInstruction*>* use = insn->GetUses().GetFirst();
- size_t use_index = use->GetIndex();
- HBasicBlock* user_block = use->GetUser()->GetBlock();
- DCHECK(use->GetUser()->IsPhi() && user_block->IsCatchBlock());
+ const HUseListNode<HInstruction*>& use = insn->GetUses().front();
+ size_t use_index = use.GetIndex();
+ HBasicBlock* user_block = use.GetUser()->GetBlock();
+ DCHECK(use.GetUser()->IsPhi() && user_block->IsCatchBlock());
for (HInstructionIterator phi_it(user_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
phi_it.Current()->AsPhi()->RemoveInputAt(use_index);
}
@@ -2190,6 +2217,7 @@
if (kIsDebugBuild) {
ScopedObjectAccess soa(Thread::Current());
DCHECK(IsValidHandle(type_handle));
+ DCHECK(!type_handle->IsErroneous());
if (!is_exact) {
DCHECK(!type_handle->CannotBeAssignedFromOtherTypes())
<< "Callers of ReferenceTypeInfo::Create should ensure is_exact is properly computed";
@@ -2391,12 +2419,11 @@
}
void HInstruction::RemoveEnvironmentUsers() {
- for (HUseIterator<HEnvironment*> use_it(GetEnvUses()); !use_it.Done(); use_it.Advance()) {
- HUseListNode<HEnvironment*>* user_node = use_it.Current();
- HEnvironment* user = user_node->GetUser();
- user->SetRawEnvAt(user_node->GetIndex(), nullptr);
+ for (const HUseListNode<HEnvironment*>& use : GetEnvUses()) {
+ HEnvironment* user = use.GetUser();
+ user->SetRawEnvAt(use.GetIndex(), nullptr);
}
- env_uses_.Clear();
+ env_uses_.clear();
}
// Returns an instruction with the opposite Boolean value from 'cond'.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 1ea2247..afb995d 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -36,6 +36,7 @@
#include "offsets.h"
#include "primitive.h"
#include "utils/array_ref.h"
+#include "utils/intrusive_forward_list.h"
namespace art {
@@ -169,7 +170,7 @@
return handle.GetReference() != nullptr;
}
- bool IsValid() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ bool IsValid() const {
return IsValidHandle(type_handle_);
}
@@ -1334,127 +1335,31 @@
const H##type* As##type() const { return this; } \
H##type* As##type() { return this; }
-template <typename T> class HUseList;
-
template <typename T>
class HUseListNode : public ArenaObject<kArenaAllocUseListNode> {
public:
- HUseListNode* GetPrevious() const { return prev_; }
- HUseListNode* GetNext() const { return next_; }
T GetUser() const { return user_; }
size_t GetIndex() const { return index_; }
void SetIndex(size_t index) { index_ = index; }
+ // Hook for the IntrusiveForwardList<>.
+ // TODO: Hide this better.
+ IntrusiveForwardListHook hook;
+
private:
HUseListNode(T user, size_t index)
- : user_(user), index_(index), prev_(nullptr), next_(nullptr) {}
+ : user_(user), index_(index) {}
T const user_;
size_t index_;
- HUseListNode<T>* prev_;
- HUseListNode<T>* next_;
- friend class HUseList<T>;
+ friend class HInstruction;
DISALLOW_COPY_AND_ASSIGN(HUseListNode);
};
template <typename T>
-class HUseList : public ValueObject {
- public:
- HUseList() : first_(nullptr) {}
-
- void Clear() {
- first_ = nullptr;
- }
-
- // Adds a new entry at the beginning of the use list and returns
- // the newly created node.
- HUseListNode<T>* AddUse(T user, size_t index, ArenaAllocator* arena) {
- HUseListNode<T>* new_node = new (arena) HUseListNode<T>(user, index);
- if (IsEmpty()) {
- first_ = new_node;
- } else {
- first_->prev_ = new_node;
- new_node->next_ = first_;
- first_ = new_node;
- }
- return new_node;
- }
-
- HUseListNode<T>* GetFirst() const {
- return first_;
- }
-
- void Remove(HUseListNode<T>* node) {
- DCHECK(node != nullptr);
- DCHECK(Contains(node));
-
- if (node->prev_ != nullptr) {
- node->prev_->next_ = node->next_;
- }
- if (node->next_ != nullptr) {
- node->next_->prev_ = node->prev_;
- }
- if (node == first_) {
- first_ = node->next_;
- }
- }
-
- bool Contains(const HUseListNode<T>* node) const {
- if (node == nullptr) {
- return false;
- }
- for (HUseListNode<T>* current = first_; current != nullptr; current = current->GetNext()) {
- if (current == node) {
- return true;
- }
- }
- return false;
- }
-
- bool IsEmpty() const {
- return first_ == nullptr;
- }
-
- bool HasOnlyOneUse() const {
- return first_ != nullptr && first_->next_ == nullptr;
- }
-
- size_t SizeSlow() const {
- size_t count = 0;
- for (HUseListNode<T>* current = first_; current != nullptr; current = current->GetNext()) {
- ++count;
- }
- return count;
- }
-
- private:
- HUseListNode<T>* first_;
-};
-
-template<typename T>
-class HUseIterator : public ValueObject {
- public:
- explicit HUseIterator(const HUseList<T>& uses) : current_(uses.GetFirst()) {}
-
- bool Done() const { return current_ == nullptr; }
-
- void Advance() {
- DCHECK(!Done());
- current_ = current_->GetNext();
- }
-
- HUseListNode<T>* Current() const {
- DCHECK(!Done());
- return current_;
- }
-
- private:
- HUseListNode<T>* current_;
-
- friend class HValue;
-};
+using HUseList = IntrusiveForwardList<HUseListNode<T>>;
// This class is used by HEnvironment and HInstruction classes to record the
// instructions they use and pointers to the corresponding HUseListNodes kept
@@ -1462,25 +1367,26 @@
template <typename T>
class HUserRecord : public ValueObject {
public:
- HUserRecord() : instruction_(nullptr), use_node_(nullptr) {}
- explicit HUserRecord(HInstruction* instruction) : instruction_(instruction), use_node_(nullptr) {}
+ HUserRecord() : instruction_(nullptr), before_use_node_() {}
+ explicit HUserRecord(HInstruction* instruction) : instruction_(instruction), before_use_node_() {}
- HUserRecord(const HUserRecord<T>& old_record, HUseListNode<T>* use_node)
- : instruction_(old_record.instruction_), use_node_(use_node) {
+ HUserRecord(const HUserRecord<T>& old_record, typename HUseList<T>::iterator before_use_node)
+ : HUserRecord(old_record.instruction_, before_use_node) {}
+ HUserRecord(HInstruction* instruction, typename HUseList<T>::iterator before_use_node)
+ : instruction_(instruction), before_use_node_(before_use_node) {
DCHECK(instruction_ != nullptr);
- DCHECK(use_node_ != nullptr);
- DCHECK(old_record.use_node_ == nullptr);
}
HInstruction* GetInstruction() const { return instruction_; }
- HUseListNode<T>* GetUseNode() const { return use_node_; }
+ typename HUseList<T>::iterator GetBeforeUseNode() const { return before_use_node_; }
+ typename HUseList<T>::iterator GetUseNode() const { return ++GetBeforeUseNode(); }
private:
// Instruction used by the user.
HInstruction* instruction_;
- // Corresponding entry in the use list kept by 'instruction_'.
- HUseListNode<T>* use_node_;
+ // Iterator before the corresponding entry in the use list kept by 'instruction_'.
+ typename HUseList<T>::iterator before_use_node_;
};
/**
@@ -1805,14 +1711,6 @@
}
private:
- // Record instructions' use entries of this environment for constant-time removal.
- // It should only be called by HInstruction when a new environment use is added.
- void RecordEnvUse(HUseListNode<HEnvironment*>* env_use) {
- DCHECK(env_use->GetUser() == this);
- size_t index = env_use->GetIndex();
- vregs_[index] = HUserRecord<HEnvironment*>(vregs_[index], env_use);
- }
-
ArenaVector<HUserRecord<HEnvironment*>> vregs_;
ArenaVector<Location> locations_;
HEnvironment* parent_;
@@ -1916,36 +1814,44 @@
ReferenceTypeInfo GetReferenceTypeInfo() const {
DCHECK_EQ(GetType(), Primitive::kPrimNot);
return ReferenceTypeInfo::CreateUnchecked(reference_type_handle_,
- GetPackedFlag<kFlagReferenceTypeIsExact>());;
+ GetPackedFlag<kFlagReferenceTypeIsExact>());
}
void AddUseAt(HInstruction* user, size_t index) {
DCHECK(user != nullptr);
- HUseListNode<HInstruction*>* use =
- uses_.AddUse(user, index, GetBlock()->GetGraph()->GetArena());
- user->SetRawInputRecordAt(index, HUserRecord<HInstruction*>(user->InputRecordAt(index), use));
+ // Note: fixup_end remains valid across push_front().
+ auto fixup_end = uses_.empty() ? uses_.begin() : ++uses_.begin();
+ HUseListNode<HInstruction*>* new_node =
+ new (GetBlock()->GetGraph()->GetArena()) HUseListNode<HInstruction*>(user, index);
+ uses_.push_front(*new_node);
+ FixUpUserRecordsAfterUseInsertion(fixup_end);
}
void AddEnvUseAt(HEnvironment* user, size_t index) {
DCHECK(user != nullptr);
- HUseListNode<HEnvironment*>* env_use =
- env_uses_.AddUse(user, index, GetBlock()->GetGraph()->GetArena());
- user->RecordEnvUse(env_use);
+ // Note: env_fixup_end remains valid across push_front().
+ auto env_fixup_end = env_uses_.empty() ? env_uses_.begin() : ++env_uses_.begin();
+ HUseListNode<HEnvironment*>* new_node =
+ new (GetBlock()->GetGraph()->GetArena()) HUseListNode<HEnvironment*>(user, index);
+ env_uses_.push_front(*new_node);
+ FixUpUserRecordsAfterEnvUseInsertion(env_fixup_end);
}
void RemoveAsUserOfInput(size_t input) {
HUserRecord<HInstruction*> input_use = InputRecordAt(input);
- input_use.GetInstruction()->uses_.Remove(input_use.GetUseNode());
+ HUseList<HInstruction*>::iterator before_use_node = input_use.GetBeforeUseNode();
+ input_use.GetInstruction()->uses_.erase_after(before_use_node);
+ input_use.GetInstruction()->FixUpUserRecordsAfterUseRemoval(before_use_node);
}
const HUseList<HInstruction*>& GetUses() const { return uses_; }
const HUseList<HEnvironment*>& GetEnvUses() const { return env_uses_; }
- bool HasUses() const { return !uses_.IsEmpty() || !env_uses_.IsEmpty(); }
- bool HasEnvironmentUses() const { return !env_uses_.IsEmpty(); }
- bool HasNonEnvironmentUses() const { return !uses_.IsEmpty(); }
+ bool HasUses() const { return !uses_.empty() || !env_uses_.empty(); }
+ bool HasEnvironmentUses() const { return !env_uses_.empty(); }
+ bool HasNonEnvironmentUses() const { return !uses_.empty(); }
bool HasOnlyOneNonEnvironmentUse() const {
- return !HasEnvironmentUses() && GetUses().HasOnlyOneUse();
+ return !HasEnvironmentUses() && GetUses().HasExactlyOneElement();
}
// Does this instruction strictly dominate `other_instruction`?
@@ -2147,7 +2053,45 @@
}
private:
- void RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use_node) { env_uses_.Remove(use_node); }
+ void FixUpUserRecordsAfterUseInsertion(HUseList<HInstruction*>::iterator fixup_end) {
+ auto before_use_node = uses_.before_begin();
+ for (auto use_node = uses_.begin(); use_node != fixup_end; ++use_node) {
+ HInstruction* user = use_node->GetUser();
+ size_t input_index = use_node->GetIndex();
+ user->SetRawInputRecordAt(input_index, HUserRecord<HInstruction*>(this, before_use_node));
+ before_use_node = use_node;
+ }
+ }
+
+ void FixUpUserRecordsAfterUseRemoval(HUseList<HInstruction*>::iterator before_use_node) {
+ auto next = ++HUseList<HInstruction*>::iterator(before_use_node);
+ if (next != uses_.end()) {
+ HInstruction* next_user = next->GetUser();
+ size_t next_index = next->GetIndex();
+ DCHECK(next_user->InputRecordAt(next_index).GetInstruction() == this);
+ next_user->SetRawInputRecordAt(next_index, HUserRecord<HInstruction*>(this, before_use_node));
+ }
+ }
+
+ void FixUpUserRecordsAfterEnvUseInsertion(HUseList<HEnvironment*>::iterator env_fixup_end) {
+ auto before_env_use_node = env_uses_.before_begin();
+ for (auto env_use_node = env_uses_.begin(); env_use_node != env_fixup_end; ++env_use_node) {
+ HEnvironment* user = env_use_node->GetUser();
+ size_t input_index = env_use_node->GetIndex();
+ user->vregs_[input_index] = HUserRecord<HEnvironment*>(this, before_env_use_node);
+ before_env_use_node = env_use_node;
+ }
+ }
+
+ void FixUpUserRecordsAfterEnvUseRemoval(HUseList<HEnvironment*>::iterator before_env_use_node) {
+ auto next = ++HUseList<HEnvironment*>::iterator(before_env_use_node);
+ if (next != env_uses_.end()) {
+ HEnvironment* next_user = next->GetUser();
+ size_t next_index = next->GetIndex();
+ DCHECK(next_user->vregs_[next_index].GetInstruction() == this);
+ next_user->vregs_[next_index] = HUserRecord<HEnvironment*>(this, before_env_use_node);
+ }
+ }
HInstruction* previous_;
HInstruction* next_;
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
index 764f5fe..d4e2a58 100644
--- a/compiler/optimizing/nodes_test.cc
+++ b/compiler/optimizing/nodes_test.cc
@@ -91,7 +91,7 @@
entry->InsertInstructionBefore(to_insert, parameter2);
ASSERT_TRUE(parameter1->HasUses());
- ASSERT_TRUE(parameter1->GetUses().HasOnlyOneUse());
+ ASSERT_TRUE(parameter1->GetUses().HasExactlyOneElement());
}
/**
@@ -115,7 +115,7 @@
entry->AddInstruction(to_add);
ASSERT_TRUE(parameter->HasUses());
- ASSERT_TRUE(parameter->GetUses().HasOnlyOneUse());
+ ASSERT_TRUE(parameter->GetUses().HasExactlyOneElement());
}
TEST(Node, ParentEnvironment) {
@@ -134,7 +134,7 @@
entry->AddInstruction(new (&allocator) HExit());
ASSERT_TRUE(parameter1->HasUses());
- ASSERT_TRUE(parameter1->GetUses().HasOnlyOneUse());
+ ASSERT_TRUE(parameter1->GetUses().HasExactlyOneElement());
HEnvironment* environment = new (&allocator) HEnvironment(
&allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, with_environment);
@@ -145,7 +145,7 @@
with_environment->SetRawEnvironment(environment);
ASSERT_TRUE(parameter1->HasEnvironmentUses());
- ASSERT_TRUE(parameter1->GetEnvUses().HasOnlyOneUse());
+ ASSERT_TRUE(parameter1->GetEnvUses().HasExactlyOneElement());
HEnvironment* parent1 = new (&allocator) HEnvironment(
&allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0, kStatic, nullptr);
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index fc72727..dcc89e8 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -63,8 +63,8 @@
void PrepareForRegisterAllocation::VisitClinitCheck(HClinitCheck* check) {
// Try to find a static invoke or a new-instance from which this check originated.
HInstruction* implicit_clinit = nullptr;
- for (HUseIterator<HInstruction*> it(check->GetUses()); !it.Done(); it.Advance()) {
- HInstruction* user = it.Current()->GetUser();
+ for (const HUseListNode<HInstruction*>& use : check->GetUses()) {
+ HInstruction* user = use.GetUser();
if ((user->IsInvokeStaticOrDirect() || user->IsNewInstance()) &&
CanMoveClinitCheck(check, user)) {
implicit_clinit = user;
@@ -85,11 +85,12 @@
// If we found a static invoke or new-instance for merging, remove the check
// from dominated static invokes.
if (implicit_clinit != nullptr) {
- for (HUseIterator<HInstruction*> it(check->GetUses()); !it.Done(); ) {
- HInstruction* user = it.Current()->GetUser();
+ const HUseList<HInstruction*>& uses = check->GetUses();
+ for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
+ HInstruction* user = it->GetUser();
// All other uses must be dominated.
DCHECK(implicit_clinit->StrictlyDominates(user) || (implicit_clinit == user));
- it.Advance(); // Advance before we remove the node, reference to the next node is preserved.
+ ++it; // Advance before we remove the node, reference to the next node is preserved.
if (user->IsInvokeStaticOrDirect()) {
user->AsInvokeStaticOrDirect()->RemoveExplicitClinitCheck(
HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
@@ -159,7 +160,7 @@
void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) {
if (condition->HasOnlyOneNonEnvironmentUse()) {
- HInstruction* user = condition->GetUses().GetFirst()->GetUser();
+ HInstruction* user = condition->GetUses().front().GetUser();
if (CanEmitConditionAt(condition, user)) {
condition->MarkEmittedAtUseSite();
}
diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h
index 429e6e3..ee32518 100644
--- a/compiler/optimizing/pretty_printer.h
+++ b/compiler/optimizing/pretty_printer.h
@@ -55,13 +55,13 @@
if (instruction->HasUses()) {
PrintString(" [");
bool first = true;
- for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
+ for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
if (first) {
first = false;
} else {
PrintString(", ");
}
- PrintInt(it.Current()->GetUser()->GetId());
+ PrintInt(use.GetUser()->GetId());
}
PrintString("]");
}
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 95f10e0..04c9ff9 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -23,6 +23,17 @@
namespace art {
+static inline mirror::DexCache* FindDexCacheWithHint(Thread* self,
+ const DexFile& dex_file,
+ Handle<mirror::DexCache> hint_dex_cache)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (LIKELY(hint_dex_cache->GetDexFile() == &dex_file)) {
+ return hint_dex_cache.Get();
+ } else {
+ return Runtime::Current()->GetClassLinker()->FindDexCache(self, dex_file);
+ }
+}
+
static inline ReferenceTypeInfo::TypeHandle GetRootHandle(StackHandleScopeCollection* handles,
ClassLinker::ClassRoot class_root,
ReferenceTypeInfo::TypeHandle* cache) {
@@ -54,10 +65,12 @@
class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor {
public:
RTPVisitor(HGraph* graph,
+ Handle<mirror::DexCache> hint_dex_cache,
HandleCache* handle_cache,
ArenaVector<HInstruction*>* worklist,
bool is_first_run)
: HGraphDelegateVisitor(graph),
+ hint_dex_cache_(hint_dex_cache),
handle_cache_(handle_cache),
worklist_(worklist),
is_first_run_(is_first_run) {}
@@ -70,7 +83,8 @@
void VisitNewArray(HNewArray* instr) OVERRIDE;
void VisitParameterValue(HParameterValue* instr) OVERRIDE;
void UpdateFieldAccessTypeInfo(HInstruction* instr, const FieldInfo& info);
- void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass, bool is_exact);
+ void SetClassAsTypeInfo(HInstruction* instr, mirror::Class* klass, bool is_exact)
+ SHARED_REQUIRES(Locks::mutator_lock_);
void VisitInstanceFieldGet(HInstanceFieldGet* instr) OVERRIDE;
void VisitStaticFieldGet(HStaticFieldGet* instr) OVERRIDE;
void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instr) OVERRIDE;
@@ -86,16 +100,19 @@
bool is_exact);
private:
+ Handle<mirror::DexCache> hint_dex_cache_;
HandleCache* handle_cache_;
ArenaVector<HInstruction*>* worklist_;
const bool is_first_run_;
};
ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
+ Handle<mirror::DexCache> hint_dex_cache,
StackHandleScopeCollection* handles,
bool is_first_run,
const char* name)
: HOptimization(graph, name),
+ hint_dex_cache_(hint_dex_cache),
handle_cache_(handles),
worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)),
is_first_run_(is_first_run) {
@@ -130,7 +147,7 @@
}
void ReferenceTypePropagation::Visit(HInstruction* instruction) {
- RTPVisitor visitor(graph_, &handle_cache_, &worklist_, is_first_run_);
+ RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_);
instruction->Accept(&visitor);
}
@@ -149,7 +166,7 @@
}
void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
- RTPVisitor visitor(graph_, &handle_cache_, &worklist_, is_first_run_);
+ RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_);
// Handle Phis first as there might be instructions in the same block who depend on them.
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
VisitPhi(it.Current()->AsPhi());
@@ -187,8 +204,8 @@
if (existing_bound_type->GetUpperBound().IsSupertypeOf(upper_bound)) {
if (kIsDebugBuild) {
// Check that the existing HBoundType dominates all the uses.
- for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
- HInstruction* user = it.Current()->GetUser();
+ for (const HUseListNode<HInstruction*>& use : obj->GetUses()) {
+ HInstruction* user = use.GetUser();
if (dominator_instr != nullptr) {
DCHECK(!dominator_instr->StrictlyDominates(user)
|| user == existing_bound_type
@@ -242,8 +259,12 @@
? ifInstruction->IfTrueSuccessor()
: ifInstruction->IfFalseSuccessor();
- for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
- HInstruction* user = it.Current()->GetUser();
+ const HUseList<HInstruction*>& uses = obj->GetUses();
+ for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
+ HInstruction* user = it->GetUser();
+ size_t index = it->GetIndex();
+ // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
+ ++it;
if (notNullBlock->Dominates(user->GetBlock())) {
if (bound_type == nullptr) {
ScopedObjectAccess soa(Thread::Current());
@@ -264,7 +285,7 @@
break;
}
}
- user->ReplaceInput(bound_type, it.Current()->GetIndex());
+ user->ReplaceInput(bound_type, index);
}
}
}
@@ -358,7 +379,6 @@
HLoadClass* load_class = instanceOf->InputAt(1)->AsLoadClass();
ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
{
- ScopedObjectAccess soa(Thread::Current());
if (!class_rti.IsValid()) {
// He have loaded an unresolved class. Don't bother bounding the type.
return;
@@ -379,8 +399,12 @@
return;
}
DCHECK(!obj->IsLoadClass()) << "We should not replace HLoadClass instructions";
- for (HUseIterator<HInstruction*> it(obj->GetUses()); !it.Done(); it.Advance()) {
- HInstruction* user = it.Current()->GetUser();
+ const HUseList<HInstruction*>& uses = obj->GetUses();
+ for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
+ HInstruction* user = it->GetUser();
+ size_t index = it->GetIndex();
+ // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
+ ++it;
if (instanceOfTrueBlock->Dominates(user->GetBlock())) {
if (bound_type == nullptr) {
ScopedObjectAccess soa(Thread::Current());
@@ -396,7 +420,7 @@
break;
}
}
- user->ReplaceInput(bound_type, it.Current()->GetIndex());
+ user->ReplaceInput(bound_type, index);
}
}
}
@@ -409,10 +433,10 @@
if (kIsDebugBuild) {
HInvoke* invoke = instr->AsInvoke();
ClassLinker* cl = Runtime::Current()->GetClassLinker();
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<2> hs(soa.Self());
+ Thread* self = Thread::Current();
+ StackHandleScope<2> hs(self);
Handle<mirror::DexCache> dex_cache(
- hs.NewHandle(cl->FindDexCache(soa.Self(), invoke->GetDexFile(), false)));
+ hs.NewHandle(FindDexCacheWithHint(self, invoke->GetDexFile(), hint_dex_cache_)));
// Use a null loader. We should probably use the compiling method's class loader,
// but then we would need to pass it to RTPVisitor just for this debug check. Since
// the method is from the String class, the null loader is good enough.
@@ -430,10 +454,14 @@
instr->SetReferenceTypeInfo(
ReferenceTypeInfo::Create(handle_cache_->GetStringClassHandle(), /* is_exact */ true));
} else if (klass != nullptr) {
- ScopedObjectAccess soa(Thread::Current());
- ReferenceTypeInfo::TypeHandle handle = handle_cache_->NewHandle(klass);
- is_exact = is_exact || handle->CannotBeAssignedFromOtherTypes();
- instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact));
+ if (klass->IsErroneous()) {
+ // Set inexact object type for erroneous types.
+ instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
+ } else {
+ ReferenceTypeInfo::TypeHandle handle = handle_cache_->NewHandle(klass);
+ is_exact = is_exact || handle->CannotBeAssignedFromOtherTypes();
+ instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact));
+ }
} else {
instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
}
@@ -446,8 +474,7 @@
DCHECK_EQ(instr->GetType(), Primitive::kPrimNot);
ScopedObjectAccess soa(Thread::Current());
- mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(
- soa.Self(), dex_file, false);
+ mirror::DexCache* dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_);
// Get type from dex cache assuming it was populated by the verifier.
SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact);
}
@@ -460,24 +487,24 @@
UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
}
-static mirror::Class* GetClassFromDexCache(Thread* self, const DexFile& dex_file, uint16_t type_idx)
+static mirror::Class* GetClassFromDexCache(Thread* self,
+ const DexFile& dex_file,
+ uint16_t type_idx,
+ Handle<mirror::DexCache> hint_dex_cache)
SHARED_REQUIRES(Locks::mutator_lock_) {
- mirror::DexCache* dex_cache =
- Runtime::Current()->GetClassLinker()->FindDexCache(self, dex_file, /* allow_failure */ true);
- if (dex_cache == nullptr) {
- // Dex cache could not be found. This should only happen during gtests.
- return nullptr;
- }
+ mirror::DexCache* dex_cache = FindDexCacheWithHint(self, dex_file, hint_dex_cache);
// Get type from dex cache assuming it was populated by the verifier.
return dex_cache->GetResolvedType(type_idx);
}
void ReferenceTypePropagation::RTPVisitor::VisitParameterValue(HParameterValue* instr) {
- ScopedObjectAccess soa(Thread::Current());
// We check if the existing type is valid: the inliner may have set it.
if (instr->GetType() == Primitive::kPrimNot && !instr->GetReferenceTypeInfo().IsValid()) {
- mirror::Class* resolved_class =
- GetClassFromDexCache(soa.Self(), instr->GetDexFile(), instr->GetTypeIndex());
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::Class* resolved_class = GetClassFromDexCache(soa.Self(),
+ instr->GetDexFile(),
+ instr->GetTypeIndex(),
+ hint_dex_cache_);
SetClassAsTypeInfo(instr, resolved_class, /* is_exact */ false);
}
}
@@ -532,9 +559,11 @@
void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) {
ScopedObjectAccess soa(Thread::Current());
// Get type from dex cache assuming it was populated by the verifier.
- mirror::Class* resolved_class =
- GetClassFromDexCache(soa.Self(), instr->GetDexFile(), instr->GetTypeIndex());
- if (resolved_class != nullptr) {
+ mirror::Class* resolved_class = GetClassFromDexCache(soa.Self(),
+ instr->GetDexFile(),
+ instr->GetTypeIndex(),
+ hint_dex_cache_);
+ if (resolved_class != nullptr && !resolved_class->IsErroneous()) {
instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(
handle_cache_->NewHandle(resolved_class), /* is_exact */ true));
}
@@ -567,7 +596,6 @@
}
void ReferenceTypePropagation::RTPVisitor::VisitNullCheck(HNullCheck* instr) {
- ScopedObjectAccess soa(Thread::Current());
ReferenceTypeInfo parent_rti = instr->InputAt(0)->GetReferenceTypeInfo();
if (parent_rti.IsValid()) {
instr->SetReferenceTypeInfo(parent_rti);
@@ -575,10 +603,9 @@
}
void ReferenceTypePropagation::RTPVisitor::VisitBoundType(HBoundType* instr) {
- ScopedObjectAccess soa(Thread::Current());
-
ReferenceTypeInfo class_rti = instr->GetUpperBound();
if (class_rti.IsValid()) {
+ ScopedObjectAccess soa(Thread::Current());
// Narrow the type as much as possible.
HInstruction* obj = instr->InputAt(0);
ReferenceTypeInfo obj_rti = obj->GetReferenceTypeInfo();
@@ -609,8 +636,6 @@
}
void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast) {
- ScopedObjectAccess soa(Thread::Current());
-
HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
HBoundType* bound_type = check_cast->GetNext()->AsBoundType();
@@ -645,7 +670,6 @@
// point the interpreter jumps to that loop header.
return;
}
- ScopedObjectAccess soa(Thread::Current());
// Set the initial type for the phi. Use the non back edge input for reaching
// a fixed point faster.
HInstruction* first_input = phi->InputAt(0);
@@ -718,7 +742,7 @@
}
Handle<mirror::Class> handle = parent_rti.GetTypeHandle();
- if (handle->IsObjectArrayClass()) {
+ if (handle->IsObjectArrayClass() && !handle->GetComponentType()->IsErroneous()) {
ReferenceTypeInfo::TypeHandle component_handle =
handle_cache->NewHandle(handle->GetComponentType());
bool is_exact = component_handle->CannotBeAssignedFromOtherTypes();
@@ -760,7 +784,8 @@
ScopedObjectAccess soa(Thread::Current());
ClassLinker* cl = Runtime::Current()->GetClassLinker();
- mirror::DexCache* dex_cache = cl->FindDexCache(soa.Self(), instr->GetDexFile());
+ mirror::DexCache* dex_cache =
+ FindDexCacheWithHint(soa.Self(), instr->GetDexFile(), hint_dex_cache_);
size_t pointer_size = cl->GetImagePointerSize();
ArtMethod* method = dex_cache->GetResolvedMethod(instr->GetDexMethodIndex(), pointer_size);
mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false, pointer_size);
@@ -887,8 +912,8 @@
}
void ReferenceTypePropagation::AddDependentInstructionsToWorklist(HInstruction* instruction) {
- for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
- HInstruction* user = it.Current()->GetUser();
+ for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
+ HInstruction* user = use.GetUser();
if ((user->IsPhi() && user->AsPhi()->IsLive())
|| user->IsBoundType()
|| user->IsNullCheck()
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 028a6fc..2106be6 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -32,6 +32,7 @@
class ReferenceTypePropagation : public HOptimization {
public:
ReferenceTypePropagation(HGraph* graph,
+ Handle<mirror::DexCache> hint_dex_cache,
StackHandleScopeCollection* handles,
bool is_first_run,
const char* name = kReferenceTypePropagationPassName);
@@ -90,6 +91,10 @@
void ValidateTypes();
+ // Note: hint_dex_cache_ is usually, but not necessarily, the dex cache associated with
+ // graph_->GetDexFile(). Since we may look up also in other dex files, it's used only
+ // as a hint, to reduce the number of calls to the costly ClassLinker::FindDexCache().
+ Handle<mirror::DexCache> hint_dex_cache_;
HandleCache handle_cache_;
ArenaVector<HInstruction*> worklist_;
@@ -99,6 +104,8 @@
static constexpr size_t kDefaultWorklistSize = 8;
+ friend class ReferenceTypePropagationTest;
+
DISALLOW_COPY_AND_ASSIGN(ReferenceTypePropagation);
};
diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc
new file mode 100644
index 0000000..7649b50
--- /dev/null
+++ b/compiler/optimizing/reference_type_propagation_test.cc
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/arena_allocator.h"
+#include "builder.h"
+#include "nodes.h"
+#include "object_lock.h"
+#include "optimizing_unit_test.h"
+#include "reference_type_propagation.h"
+
+namespace art {
+
+/**
+ * Fixture class for unit testing the ReferenceTypePropagation phase. Used to verify the
+ * functionality of methods and situations that are hard to set up with checker tests.
+ */
+class ReferenceTypePropagationTest : public CommonCompilerTest {
+ public:
+ ReferenceTypePropagationTest() : pool_(), allocator_(&pool_) {
+ graph_ = CreateGraph(&allocator_);
+ }
+
+ ~ReferenceTypePropagationTest() { }
+
+ void SetupPropagation(StackHandleScopeCollection* handles) {
+ graph_->InitializeInexactObjectRTI(handles);
+ propagation_ = new (&allocator_) ReferenceTypePropagation(graph_,
+ Handle<mirror::DexCache>(),
+ handles,
+ true,
+ "test_prop");
+ }
+
+ // Relay method to merge type in reference type propagation.
+ ReferenceTypeInfo MergeTypes(const ReferenceTypeInfo& a,
+ const ReferenceTypeInfo& b) SHARED_REQUIRES(Locks::mutator_lock_) {
+ return propagation_->MergeTypes(a, b);
+ }
+
+ // Helper method to construct an invalid type.
+ ReferenceTypeInfo InvalidType() {
+ return ReferenceTypeInfo::CreateInvalid();
+ }
+
+ // Helper method to construct the Object type.
+ ReferenceTypeInfo ObjectType(bool is_exact = true) SHARED_REQUIRES(Locks::mutator_lock_) {
+ return ReferenceTypeInfo::Create(propagation_->handle_cache_.GetObjectClassHandle(), is_exact);
+ }
+
+ // Helper method to construct the String type.
+ ReferenceTypeInfo StringType(bool is_exact = true) SHARED_REQUIRES(Locks::mutator_lock_) {
+ return ReferenceTypeInfo::Create(propagation_->handle_cache_.GetStringClassHandle(), is_exact);
+ }
+
+ // General building fields.
+ ArenaPool pool_;
+ ArenaAllocator allocator_;
+ HGraph* graph_;
+
+ ReferenceTypePropagation* propagation_;
+};
+
+//
+// The actual ReferenceTypePropgation unit tests.
+//
+
+TEST_F(ReferenceTypePropagationTest, ProperSetup) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScopeCollection handles(soa.Self());
+ SetupPropagation(&handles);
+
+ EXPECT_TRUE(propagation_ != nullptr);
+ EXPECT_TRUE(graph_->GetInexactObjectRti().IsEqual(ObjectType(false)));
+}
+
+TEST_F(ReferenceTypePropagationTest, MergeInvalidTypes) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScopeCollection handles(soa.Self());
+ SetupPropagation(&handles);
+
+ // Two invalid types.
+ ReferenceTypeInfo t1(MergeTypes(InvalidType(), InvalidType()));
+ EXPECT_FALSE(t1.IsValid());
+ EXPECT_FALSE(t1.IsExact());
+ EXPECT_TRUE(t1.IsEqual(InvalidType()));
+
+ // Valid type on right.
+ ReferenceTypeInfo t2(MergeTypes(InvalidType(), ObjectType()));
+ EXPECT_TRUE(t2.IsValid());
+ EXPECT_TRUE(t2.IsExact());
+ EXPECT_TRUE(t2.IsEqual(ObjectType()));
+ ReferenceTypeInfo t3(MergeTypes(InvalidType(), StringType()));
+ EXPECT_TRUE(t3.IsValid());
+ EXPECT_TRUE(t3.IsExact());
+ EXPECT_TRUE(t3.IsEqual(StringType()));
+
+ // Valid type on left.
+ ReferenceTypeInfo t4(MergeTypes(ObjectType(), InvalidType()));
+ EXPECT_TRUE(t4.IsValid());
+ EXPECT_TRUE(t4.IsExact());
+ EXPECT_TRUE(t4.IsEqual(ObjectType()));
+ ReferenceTypeInfo t5(MergeTypes(StringType(), InvalidType()));
+ EXPECT_TRUE(t5.IsValid());
+ EXPECT_TRUE(t5.IsExact());
+ EXPECT_TRUE(t5.IsEqual(StringType()));
+}
+
+TEST_F(ReferenceTypePropagationTest, MergeValidTypes) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScopeCollection handles(soa.Self());
+ SetupPropagation(&handles);
+
+ // Same types.
+ ReferenceTypeInfo t1(MergeTypes(ObjectType(), ObjectType()));
+ EXPECT_TRUE(t1.IsValid());
+ EXPECT_TRUE(t1.IsExact());
+ EXPECT_TRUE(t1.IsEqual(ObjectType()));
+ ReferenceTypeInfo t2(MergeTypes(StringType(), StringType()));
+ EXPECT_TRUE(t2.IsValid());
+ EXPECT_TRUE(t2.IsExact());
+ EXPECT_TRUE(t2.IsEqual(StringType()));
+
+ // Left is super class of right.
+ ReferenceTypeInfo t3(MergeTypes(ObjectType(), StringType()));
+ EXPECT_TRUE(t3.IsValid());
+ EXPECT_FALSE(t3.IsExact());
+ EXPECT_TRUE(t3.IsEqual(ObjectType(false)));
+
+ // Right is super class of left.
+ ReferenceTypeInfo t4(MergeTypes(StringType(), ObjectType()));
+ EXPECT_TRUE(t4.IsValid());
+ EXPECT_FALSE(t4.IsExact());
+ EXPECT_TRUE(t4.IsEqual(ObjectType(false)));
+
+ // Same types, but one or both are inexact.
+ ReferenceTypeInfo t5(MergeTypes(ObjectType(false), ObjectType()));
+ EXPECT_TRUE(t5.IsValid());
+ EXPECT_FALSE(t5.IsExact());
+ EXPECT_TRUE(t5.IsEqual(ObjectType(false)));
+ ReferenceTypeInfo t6(MergeTypes(ObjectType(), ObjectType(false)));
+ EXPECT_TRUE(t6.IsValid());
+ EXPECT_FALSE(t6.IsExact());
+ EXPECT_TRUE(t6.IsEqual(ObjectType(false)));
+ ReferenceTypeInfo t7(MergeTypes(ObjectType(false), ObjectType(false)));
+ EXPECT_TRUE(t7.IsValid());
+ EXPECT_FALSE(t7.IsExact());
+ EXPECT_TRUE(t7.IsEqual(ObjectType(false)));
+}
+
+} // namespace art
+
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 2fe2f20..c2aa0c0 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -108,8 +108,8 @@
// marked dead/conflicting too, so we add them to the worklist. Otherwise we
// add users whose type does not match and needs to be updated.
bool add_all_live_phis = instruction->IsPhi() && instruction->AsPhi()->IsDead();
- for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
- HInstruction* user = it.Current()->GetUser();
+ for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
+ HInstruction* user = use.GetUser();
if (user->IsPhi() && user->AsPhi()->IsLive()) {
if (add_all_live_phis || user->GetType() != instruction->GetType()) {
worklist->push_back(user->AsPhi());
@@ -412,27 +412,24 @@
}
static bool HasAliasInEnvironments(HInstruction* instruction) {
- for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses());
- !use_it.Done();
- use_it.Advance()) {
- HEnvironment* use = use_it.Current()->GetUser();
- HUseListNode<HEnvironment*>* next = use_it.Current()->GetNext();
- if (next != nullptr && next->GetUser() == use) {
+ HEnvironment* last_user = nullptr;
+ for (const HUseListNode<HEnvironment*>& use : instruction->GetEnvUses()) {
+ DCHECK(use.GetUser() != nullptr);
+ // Note: The first comparison (== null) always fails.
+ if (use.GetUser() == last_user) {
return true;
}
+ last_user = use.GetUser();
}
if (kIsDebugBuild) {
// Do a quadratic search to ensure same environment uses are next
// to each other.
- for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses());
- !use_it.Done();
- use_it.Advance()) {
- HUseListNode<HEnvironment*>* current = use_it.Current();
- HUseListNode<HEnvironment*>* next = current->GetNext();
- while (next != nullptr) {
+ const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
+ for (auto current = env_uses.begin(), end = env_uses.end(); current != end; ++current) {
+ auto next = current;
+ for (++next; next != end; ++next) {
DCHECK(next->GetUser() != current->GetUser());
- next = next->GetNext();
}
}
}
@@ -509,7 +506,7 @@
// 4) Compute type of reference type instructions. The pass assumes that
// NullConstant has been fixed up.
- ReferenceTypePropagation(graph_, handles_, /* is_first_run */ true).Run();
+ ReferenceTypePropagation(graph_, dex_cache_, handles_, /* is_first_run */ true).Run();
// 5) HInstructionBuilder duplicated ArrayGet instructions with ambiguous type
// (int/float or long/double) and marked ArraySets with ambiguous input type.
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index c37c28c..d7360ad 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -47,8 +47,11 @@
*/
class SsaBuilder : public ValueObject {
public:
- SsaBuilder(HGraph* graph, StackHandleScopeCollection* handles)
+ SsaBuilder(HGraph* graph,
+ Handle<mirror::DexCache> dex_cache,
+ StackHandleScopeCollection* handles)
: graph_(graph),
+ dex_cache_(dex_cache),
handles_(handles),
agets_fixed_(false),
ambiguous_agets_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)),
@@ -112,6 +115,7 @@
void RemoveRedundantUninitializedStrings();
HGraph* graph_;
+ Handle<mirror::DexCache> dex_cache_;
StackHandleScopeCollection* const handles_;
// True if types of ambiguous ArrayGets have been resolved.
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 83e9dac..5534aea 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -283,11 +283,9 @@
if (current->IsEmittedAtUseSite()) {
if (kIsDebugBuild) {
DCHECK(!current->GetLocations()->Out().IsValid());
- for (HUseIterator<HInstruction*> use_it(current->GetUses());
- !use_it.Done();
- use_it.Advance()) {
- HInstruction* user = use_it.Current()->GetUser();
- size_t index = use_it.Current()->GetIndex();
+ for (const HUseListNode<HInstruction*>& use : current->GetUses()) {
+ HInstruction* user = use.GetUser();
+ size_t index = use.GetIndex();
DCHECK(!user->GetLocations()->InAt(index).IsValid());
}
DCHECK(!current->HasEnvironmentUses());
@@ -318,7 +316,7 @@
for (uint32_t idx : live_in->Indexes()) {
HInstruction* instruction = GetInstructionFromSsaIndex(idx);
DCHECK(instruction->GetBlock()->IsEntryBlock()) << instruction->DebugName();
- DCHECK(!instruction->IsParameterValue()) << instruction->DebugName();
+ DCHECK(!instruction->IsParameterValue());
DCHECK(instruction->IsCurrentMethod() || instruction->IsConstant())
<< instruction->DebugName();
}
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 719feec..40dab74 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -971,7 +971,7 @@
bool IsLinearOrderWellFormed(const HGraph& graph) {
for (HBasicBlock* header : graph.GetBlocks()) {
- if (!header->IsLoopHeader()) {
+ if (header == nullptr || !header->IsLoopHeader()) {
continue;
}
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index 6816b6a..aeb3109 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -43,8 +43,8 @@
bool keep_alive = (graph_->IsDebuggable() && phi->HasEnvironmentUses());
if (!keep_alive) {
- for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) {
- if (!use_it.Current()->GetUser()->IsPhi()) {
+ for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
+ if (!use.GetUser()->IsPhi()) {
keep_alive = true;
break;
}
@@ -94,9 +94,8 @@
if (phi->IsDead()) {
// Make sure the phi is only used by other dead phis.
if (kIsDebugBuild) {
- for (HUseIterator<HInstruction*> use_it(phi->GetUses()); !use_it.Done();
- use_it.Advance()) {
- HInstruction* user = use_it.Current()->GetUser();
+ for (const HUseListNode<HInstruction*>& use : phi->GetUses()) {
+ HInstruction* user = use.GetUser();
DCHECK(user->IsLoopHeaderPhi());
DCHECK(user->AsPhi()->IsDead());
}
@@ -106,11 +105,9 @@
phi->RemoveAsUserOfInput(i);
}
// Remove the phi from environments that use it.
- for (HUseIterator<HEnvironment*> use_it(phi->GetEnvUses()); !use_it.Done();
- use_it.Advance()) {
- HUseListNode<HEnvironment*>* user_node = use_it.Current();
- HEnvironment* user = user_node->GetUser();
- user->SetRawEnvAt(user_node->GetIndex(), nullptr);
+ for (const HUseListNode<HEnvironment*>& use : phi->GetEnvUses()) {
+ HEnvironment* user = use.GetUser();
+ user->SetRawEnvAt(use.GetIndex(), nullptr);
}
// Delete it from the instruction list.
block->RemovePhi(phi, /*ensure_safety=*/ false);
@@ -233,9 +230,8 @@
// Because we're updating the users of this phi, we may have new candidates
// for elimination. Add phis that use this phi to the worklist.
- for (HUseIterator<HInstruction*> it(current->GetUses()); !it.Done(); it.Advance()) {
- HUseListNode<HInstruction*>* use = it.Current();
- HInstruction* user = use->GetUser();
+ for (const HUseListNode<HInstruction*>& use : current->GetUses()) {
+ HInstruction* user = use.GetUser();
if (user->IsPhi() && !ContainsElement(visited_phis_in_cycle, user->GetId())) {
worklist_.push_back(user->AsPhi());
}
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index 218bd53..4297634 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -346,7 +346,7 @@
"BasicBlock 7, pred: 6\n"
" 12: Exit\n"
"BasicBlock 8, pred: 2, 3, succ: 4\n"
- " 13: Phi(2, 1) [8, 8, 11]\n"
+ " 13: Phi(2, 1) [11, 8, 8]\n"
" 14: Goto\n";
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 26f7d0d..2c73fb8 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -256,7 +256,10 @@
for (JumpTable& table : jump_tables_) {
// Bulk ensure capacity, as this may be large.
size_t orig_size = buffer_.Size();
- buffer_.ExtendCapacity(orig_size + table.GetSize());
+ size_t required_capacity = orig_size + table.GetSize();
+ if (required_capacity > buffer_.Capacity()) {
+ buffer_.ExtendCapacity(required_capacity);
+ }
#ifndef NDEBUG
buffer_.has_ensured_capacity_ = true;
#endif
diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc
index c2aa574..e6c3a18 100644
--- a/compiler/utils/assembler.cc
+++ b/compiler/utils/assembler.cc
@@ -47,7 +47,7 @@
AssemblerBuffer::AssemblerBuffer(ArenaAllocator* arena)
: arena_(arena) {
static const size_t kInitialBufferCapacity = 4 * KB;
- contents_ = arena_->AllocArray<uint8_t>(kInitialBufferCapacity);
+ contents_ = arena_->AllocArray<uint8_t>(kInitialBufferCapacity, kArenaAllocAssembler);
cursor_ = contents_;
limit_ = ComputeLimit(contents_, kInitialBufferCapacity);
fixup_ = nullptr;
@@ -94,6 +94,7 @@
void AssemblerBuffer::ExtendCapacity(size_t min_capacity) {
size_t old_size = Size();
size_t old_capacity = Capacity();
+ DCHECK_GT(min_capacity, old_capacity);
size_t new_capacity = std::min(old_capacity * 2, old_capacity + 1 * MB);
new_capacity = std::max(new_capacity, min_capacity);
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index f70fe04..5267dc3 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -178,8 +178,8 @@
class EnsureCapacity {
public:
explicit EnsureCapacity(AssemblerBuffer* buffer) {
- if (buffer->cursor() >= buffer->limit()) {
- buffer->ExtendCapacity();
+ if (buffer->cursor() > buffer->limit()) {
+ buffer->ExtendCapacity(buffer->Size() + kMinimumGap);
}
// In debug mode, we save the assembler buffer along with the gap
// size before we start emitting to the buffer. This allows us to
@@ -219,7 +219,9 @@
class EnsureCapacity {
public:
explicit EnsureCapacity(AssemblerBuffer* buffer) {
- if (buffer->cursor() >= buffer->limit()) buffer->ExtendCapacity();
+ if (buffer->cursor() > buffer->limit()) {
+ buffer->ExtendCapacity(buffer->Size() + kMinimumGap);
+ }
}
};
@@ -233,7 +235,14 @@
// Returns the position in the instruction stream.
int GetPosition() { return cursor_ - contents_; }
- void ExtendCapacity(size_t min_capacity = 0u);
+ size_t Capacity() const {
+ CHECK_GE(limit_, contents_);
+ return (limit_ - contents_) + kMinimumGap;
+ }
+
+ // Unconditionally increase the capacity.
+ // The provided `min_capacity` must be higher than current `Capacity()`.
+ void ExtendCapacity(size_t min_capacity);
private:
// The limit is set to kMinimumGap bytes before the end of the data area.
@@ -255,10 +264,6 @@
uint8_t* cursor() const { return cursor_; }
uint8_t* limit() const { return limit_; }
- size_t Capacity() const {
- CHECK_GE(limit_, contents_);
- return (limit_ - contents_) + kMinimumGap;
- }
// Process the fixup chain starting at the given fixup. The offset is
// non-zero for fixups in the body if the preamble is non-empty.
diff --git a/compiler/utils/intrusive_forward_list.h b/compiler/utils/intrusive_forward_list.h
new file mode 100644
index 0000000..ec2c087
--- /dev/null
+++ b/compiler/utils/intrusive_forward_list.h
@@ -0,0 +1,452 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_UTILS_INTRUSIVE_FORWARD_LIST_H_
+#define ART_COMPILER_UTILS_INTRUSIVE_FORWARD_LIST_H_
+
+#include <stdint.h>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace art {
+
+struct IntrusiveForwardListHook {
+ IntrusiveForwardListHook() : next_hook(nullptr) { }
+ explicit IntrusiveForwardListHook(const IntrusiveForwardListHook* hook) : next_hook(hook) { }
+
+ // Allow copyable values but do not copy the hook, it is not part of the value.
+ IntrusiveForwardListHook(const IntrusiveForwardListHook& other ATTRIBUTE_UNUSED)
+ : next_hook(nullptr) { }
+ IntrusiveForwardListHook& operator=(const IntrusiveForwardListHook& src ATTRIBUTE_UNUSED) {
+ return *this;
+ }
+
+ mutable const IntrusiveForwardListHook* next_hook;
+};
+
+template <typename T, IntrusiveForwardListHook T::* NextPtr = &T::hook>
+class IntrusiveForwardListMemberHook;
+
+template <typename T, typename HookTraits = IntrusiveForwardListMemberHook<T>>
+class IntrusiveForwardList;
+
+template <typename T, typename HookTraits>
+class IntrusiveForwardListIterator : public std::iterator<std::forward_iterator_tag, T> {
+ public:
+ // Construct/copy/destroy (except the private constructor used by IntrusiveForwardList<>).
+ IntrusiveForwardListIterator() : hook_(nullptr) { }
+ IntrusiveForwardListIterator(const IntrusiveForwardListIterator& src) = default;
+ IntrusiveForwardListIterator& operator=(const IntrusiveForwardListIterator& src) = default;
+
+ // Conversion from iterator to const_iterator.
+ template <typename OtherT,
+ typename = typename std::enable_if<std::is_same<T, const OtherT>::value>::type>
+ IntrusiveForwardListIterator(const IntrusiveForwardListIterator<OtherT, HookTraits>& src)
+ : hook_(src.hook_) { }
+
+ // Iteration.
+ IntrusiveForwardListIterator& operator++() {
+ DCHECK(hook_ != nullptr);
+ hook_ = hook_->next_hook;
+ return *this;
+ }
+ IntrusiveForwardListIterator operator++(int) {
+ IntrusiveForwardListIterator tmp(*this);
+ ++*this;
+ return tmp;
+ }
+
+ // Dereference
+ T& operator*() const {
+ DCHECK(hook_ != nullptr);
+ return *HookTraits::GetValue(hook_);
+ }
+ T* operator->() const {
+ return &**this;
+ }
+
+ private:
+ explicit IntrusiveForwardListIterator(const IntrusiveForwardListHook* hook) : hook_(hook) { }
+
+ const IntrusiveForwardListHook* hook_;
+
+ template <typename OtherT, typename OtherTraits>
+ friend class IntrusiveForwardListIterator;
+
+ template <typename OtherT, typename OtherTraits>
+ friend class IntrusiveForwardList;
+
+ template <typename OtherT1, typename OtherT2, typename OtherTraits>
+ friend typename std::enable_if<std::is_same<const OtherT1, const OtherT2>::value, bool>::type
+ operator==(const IntrusiveForwardListIterator<OtherT1, OtherTraits>& lhs,
+ const IntrusiveForwardListIterator<OtherT2, OtherTraits>& rhs);
+};
+
+template <typename T, typename OtherT, typename HookTraits>
+typename std::enable_if<std::is_same<const T, const OtherT>::value, bool>::type operator==(
+ const IntrusiveForwardListIterator<T, HookTraits>& lhs,
+ const IntrusiveForwardListIterator<OtherT, HookTraits>& rhs) {
+ return lhs.hook_ == rhs.hook_;
+}
+
+template <typename T, typename OtherT, typename HookTraits>
+typename std::enable_if<std::is_same<const T, const OtherT>::value, bool>::type operator!=(
+ const IntrusiveForwardListIterator<T, HookTraits>& lhs,
+ const IntrusiveForwardListIterator<OtherT, HookTraits>& rhs) {
+ return !(lhs == rhs);
+}
+
+// Intrusive version of std::forward_list<>. See also slist<> in Boost.Intrusive.
+//
+// This class template provides the same interface as std::forward_list<> as long
+// as the functions are meaningful for an intrusive container; this excludes emplace
+// functions and functions taking an std::initializer_list<> as the container does
+// not construct elements.
+template <typename T, typename HookTraits>
+class IntrusiveForwardList {
+ public:
+ typedef HookTraits hook_traits;
+ typedef T value_type;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef IntrusiveForwardListIterator< T, hook_traits> iterator;
+ typedef IntrusiveForwardListIterator<const T, hook_traits> const_iterator;
+
+ // Construct/copy/destroy.
+ IntrusiveForwardList() = default;
+ template <typename InputIterator>
+ IntrusiveForwardList(InputIterator first, InputIterator last) : IntrusiveForwardList() {
+ insert_after(before_begin(), first, last);
+ }
+ IntrusiveForwardList(IntrusiveForwardList&& src) : first_(src.first_.next_hook) {
+ src.first_.next_hook = nullptr;
+ }
+ IntrusiveForwardList& operator=(const IntrusiveForwardList& src) = delete;
+ IntrusiveForwardList& operator=(IntrusiveForwardList&& src) {
+ IntrusiveForwardList tmp(std::move(src));
+ tmp.swap(*this);
+ return *this;
+ }
+ ~IntrusiveForwardList() = default;
+
+ // Iterators.
+ iterator before_begin() { return iterator(&first_); }
+ const_iterator before_begin() const { return const_iterator(&first_); }
+ iterator begin() { return iterator(first_.next_hook); }
+ const_iterator begin() const { return const_iterator(first_.next_hook); }
+ iterator end() { return iterator(nullptr); }
+ const_iterator end() const { return const_iterator(nullptr); }
+ const_iterator cbefore_begin() const { return const_iterator(&first_); }
+ const_iterator cbegin() const { return const_iterator(first_.next_hook); }
+ const_iterator cend() const { return const_iterator(nullptr); }
+
+ // Capacity.
+ bool empty() const { return begin() == end(); }
+ size_t max_size() { return static_cast<size_t>(-1); }
+
+ // Element access.
+ reference front() { return *begin(); }
+ const_reference front() const { return *begin(); }
+
+ // Modifiers.
+ template <typename InputIterator>
+ void assign(InputIterator first, InputIterator last) {
+ IntrusiveForwardList tmp(first, last);
+ tmp.swap(*this);
+ }
+ void push_front(value_type& value) {
+ insert_after(before_begin(), value);
+ }
+ void pop_front() {
+ DCHECK(!empty());
+ erase_after(before_begin());
+ }
+ iterator insert_after(const_iterator position, value_type& value) {
+ const IntrusiveForwardListHook* new_hook = hook_traits::GetHook(&value);
+ new_hook->next_hook = position.hook_->next_hook;
+ position.hook_->next_hook = new_hook;
+ return iterator(new_hook);
+ }
+ template <typename InputIterator>
+ iterator insert_after(const_iterator position, InputIterator first, InputIterator last) {
+ while (first != last) {
+ position = insert_after(position, *first++);
+ }
+ return iterator(position.hook_);
+ }
+ iterator erase_after(const_iterator position) {
+ const_iterator last = position;
+ std::advance(last, 2);
+ return erase_after(position, last);
+ }
+ iterator erase_after(const_iterator position, const_iterator last) {
+ DCHECK(position != last);
+ position.hook_->next_hook = last.hook_;
+ return iterator(last.hook_);
+ }
+ void swap(IntrusiveForwardList& other) {
+ std::swap(first_.next_hook, other.first_.next_hook);
+ }
+ void clear() {
+ first_.next_hook = nullptr;
+ }
+
+ // Operations.
+ void splice_after(const_iterator position, IntrusiveForwardList& src) {
+ DCHECK(position != end());
+ splice_after(position, src, src.before_begin(), src.end());
+ }
+ void splice_after(const_iterator position, IntrusiveForwardList&& src) {
+ splice_after(position, src); // Use l-value overload.
+ }
+ // Splice the element after `i`.
+ void splice_after(const_iterator position, IntrusiveForwardList& src, const_iterator i) {
+ // The standard specifies that this version does nothing if `position == i`
+ // or `position == ++i`. We must handle the latter here because the overload
+ // `splice_after(position, src, first, last)` does not allow `position` inside
+ // the range `(first, last)`.
+ if (++const_iterator(i) == position) {
+ return;
+ }
+ const_iterator last = i;
+ std::advance(last, 2);
+ splice_after(position, src, i, last);
+ }
+ // Splice the element after `i`.
+ void splice_after(const_iterator position, IntrusiveForwardList&& src, const_iterator i) {
+ splice_after(position, src, i); // Use l-value overload.
+ }
+ // Splice elements between `first` and `last`, i.e. open range `(first, last)`.
+ void splice_after(const_iterator position,
+ IntrusiveForwardList& src,
+ const_iterator first,
+ const_iterator last) {
+ DCHECK(position != end());
+ DCHECK(first != last);
+ if (++const_iterator(first) == last) {
+ // Nothing to do.
+ return;
+ }
+ // If position is just before end() and last is src.end(), we can finish this quickly.
+ if (++const_iterator(position) == end() && last == src.end()) {
+ position.hook_->next_hook = first.hook_->next_hook;
+ first.hook_->next_hook = nullptr;
+ return;
+ }
+ // Otherwise we need to find the position before last to fix up the hook.
+ const_iterator before_last = first;
+ while (++const_iterator(before_last) != last) {
+ ++before_last;
+ }
+ // Detach (first, last).
+ const IntrusiveForwardListHook* first_taken = first.hook_->next_hook;
+ first.hook_->next_hook = last.hook_;
+ // Attach the sequence to the new position.
+ before_last.hook_->next_hook = position.hook_->next_hook;
+ position.hook_->next_hook = first_taken;
+ }
+ // Splice elements between `first` and `last`, i.e. open range `(first, last)`.
+ void splice_after(const_iterator position,
+ IntrusiveForwardList&& src,
+ const_iterator first,
+ const_iterator last) {
+ splice_after(position, src, first, last); // Use l-value overload.
+ }
+ void remove(const value_type& value) {
+ remove_if([value](const value_type& v) { return value == v; });
+ }
+ template <typename Predicate>
+ void remove_if(Predicate pred) {
+ iterator prev = before_begin();
+ for (iterator current = begin(); current != end(); ++current) {
+ if (pred(*current)) {
+ erase_after(prev);
+ current = prev;
+ } else {
+ prev = current;
+ }
+ }
+ }
+ void unique() {
+ unique(std::equal_to<value_type>());
+ }
+ template <typename BinaryPredicate>
+ void unique(BinaryPredicate pred) {
+ if (!empty()) {
+ iterator prev = begin();
+ iterator current = prev;
+ ++current;
+ for (; current != end(); ++current) {
+ if (pred(*prev, *current)) {
+ erase_after(prev);
+ current = prev;
+ } else {
+ prev = current;
+ }
+ }
+ }
+ }
+ void merge(IntrusiveForwardList& other) {
+ merge(other, std::less<value_type>());
+ }
+ void merge(IntrusiveForwardList&& other) {
+ merge(other); // Use l-value overload.
+ }
+ template <typename Compare>
+ void merge(IntrusiveForwardList& other, Compare cmp) {
+ iterator prev = before_begin();
+ iterator current = begin();
+ iterator other_prev = other.before_begin();
+ iterator other_current = other.begin();
+ while (current != end() && other_current != other.end()) {
+ if (cmp(*other_current, *current)) {
+ ++other_current;
+ splice_after(prev, other, other_prev);
+ ++prev;
+ } else {
+ prev = current;
+ ++current;
+ }
+ DCHECK(++const_iterator(prev) == current);
+ DCHECK(++const_iterator(other_prev) == other_current);
+ }
+ splice_after(prev, other);
+ }
+ template <typename Compare>
+ void merge(IntrusiveForwardList&& other, Compare cmp) {
+ merge(other, cmp); // Use l-value overload.
+ }
+ void sort() {
+ sort(std::less<value_type>());
+ }
+ template <typename Compare>
+ void sort(Compare cmp) {
+ size_t n = std::distance(begin(), end());
+ if (n >= 2u) {
+ const_iterator middle = before_begin();
+ std::advance(middle, n / 2u);
+ IntrusiveForwardList second_half;
+ second_half.splice_after(second_half.before_begin(), *this, middle, end());
+ sort(cmp);
+ second_half.sort(cmp);
+ merge(second_half, cmp);
+ }
+ }
+ void reverse() {
+ IntrusiveForwardList reversed;
+ while (!empty()) {
+ value_type& value = front();
+ erase_after(before_begin());
+ reversed.insert_after(reversed.before_begin(), value);
+ }
+ reversed.swap(*this);
+ }
+
+ // Extensions.
+ bool HasExactlyOneElement() const {
+ return !empty() && ++begin() == end();
+ }
+ size_t SizeSlow() const {
+ return std::distance(begin(), end());
+ }
+ bool ContainsNode(const_reference node) const {
+ for (auto&& n : *this) {
+ if (std::addressof(n) == std::addressof(node)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private:
+ static IntrusiveForwardListHook* ModifiableHook(const IntrusiveForwardListHook* hook) {
+ return const_cast<IntrusiveForwardListHook*>(hook);
+ }
+
+ IntrusiveForwardListHook first_;
+};
+
+template <typename T, typename HookTraits>
+void swap(IntrusiveForwardList<T, HookTraits>& lhs, IntrusiveForwardList<T, HookTraits>& rhs) {
+ lhs.swap(rhs);
+}
+
+template <typename T, typename HookTraits>
+bool operator==(const IntrusiveForwardList<T, HookTraits>& lhs,
+ const IntrusiveForwardList<T, HookTraits>& rhs) {
+ auto lit = lhs.begin();
+ auto rit = rhs.begin();
+ for (; lit != lhs.end() && rit != rhs.end(); ++lit, ++rit) {
+ if (*lit != *rit) {
+ return false;
+ }
+ }
+ return lit == lhs.end() && rit == rhs.end();
+}
+
+template <typename T, typename HookTraits>
+bool operator!=(const IntrusiveForwardList<T, HookTraits>& lhs,
+ const IntrusiveForwardList<T, HookTraits>& rhs) {
+ return !(lhs == rhs);
+}
+
+template <typename T, typename HookTraits>
+bool operator<(const IntrusiveForwardList<T, HookTraits>& lhs,
+ const IntrusiveForwardList<T, HookTraits>& rhs) {
+ return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+}
+
+template <typename T, typename HookTraits>
+bool operator>(const IntrusiveForwardList<T, HookTraits>& lhs,
+ const IntrusiveForwardList<T, HookTraits>& rhs) {
+ return rhs < lhs;
+}
+
+template <typename T, typename HookTraits>
+bool operator<=(const IntrusiveForwardList<T, HookTraits>& lhs,
+ const IntrusiveForwardList<T, HookTraits>& rhs) {
+ return !(rhs < lhs);
+}
+
+template <typename T, typename HookTraits>
+bool operator>=(const IntrusiveForwardList<T, HookTraits>& lhs,
+ const IntrusiveForwardList<T, HookTraits>& rhs) {
+ return !(lhs < rhs);
+}
+
+template <typename T, IntrusiveForwardListHook T::* NextPtr>
+class IntrusiveForwardListMemberHook {
+ public:
+ static const IntrusiveForwardListHook* GetHook(const T* value) {
+ return &(value->*NextPtr);
+ }
+
+ static T* GetValue(const IntrusiveForwardListHook* hook) {
+ return reinterpret_cast<T*>(
+ reinterpret_cast<uintptr_t>(hook) - OFFSETOF_MEMBERPTR(T, NextPtr));
+ }
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_UTILS_INTRUSIVE_FORWARD_LIST_H_
diff --git a/compiler/utils/intrusive_forward_list_test.cc b/compiler/utils/intrusive_forward_list_test.cc
new file mode 100644
index 0000000..517142e
--- /dev/null
+++ b/compiler/utils/intrusive_forward_list_test.cc
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <forward_list>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "intrusive_forward_list.h"
+
+namespace art {
+
+struct IFLTestValue {
+ // Deliberately not explicit.
+ IFLTestValue(int v) : hook(), value(v) { } // NOLINT(runtime/explicit)
+
+ IntrusiveForwardListHook hook;
+ int value;
+};
+
+bool operator==(const IFLTestValue& lhs, const IFLTestValue& rhs) {
+ return lhs.value == rhs.value;
+}
+
+bool operator<(const IFLTestValue& lhs, const IFLTestValue& rhs) {
+ return lhs.value < rhs.value;
+}
+
+#define ASSERT_LISTS_EQUAL(expected, value) \
+ do { \
+ ASSERT_EQ(expected.empty(), value.empty()); \
+ ASSERT_EQ(std::distance(expected.begin(), expected.end()), \
+ std::distance(value.begin(), value.end())); \
+ ASSERT_TRUE(std::equal(expected.begin(), expected.end(), value.begin())); \
+ } while (false)
+
+TEST(IntrusiveForwardList, IteratorToConstIterator) {
+ IntrusiveForwardList<IFLTestValue> ifl;
+ IntrusiveForwardList<IFLTestValue>::iterator begin = ifl.begin();
+ IntrusiveForwardList<IFLTestValue>::const_iterator cbegin = ifl.cbegin();
+ IntrusiveForwardList<IFLTestValue>::const_iterator converted_begin = begin;
+ ASSERT_TRUE(converted_begin == cbegin);
+}
+
+TEST(IntrusiveForwardList, IteratorOperators) {
+ IntrusiveForwardList<IFLTestValue> ifl;
+ ASSERT_TRUE(ifl.begin() == ifl.cbegin());
+ ASSERT_FALSE(ifl.begin() != ifl.cbegin());
+ ASSERT_TRUE(ifl.end() == ifl.cend());
+ ASSERT_FALSE(ifl.end() != ifl.cend());
+
+ ASSERT_TRUE(ifl.begin() == ifl.end()); // Empty.
+ ASSERT_FALSE(ifl.begin() != ifl.end()); // Empty.
+
+ IFLTestValue value(1);
+ ifl.insert_after(ifl.cbefore_begin(), value);
+
+ ASSERT_FALSE(ifl.begin() == ifl.end()); // Not empty.
+ ASSERT_TRUE(ifl.begin() != ifl.end()); // Not empty.
+}
+
+TEST(IntrusiveForwardList, ConstructRange) {
+ std::forward_list<int> ref({ 1, 2, 7 });
+ std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+ IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+}
+
+TEST(IntrusiveForwardList, Assign) {
+ std::forward_list<int> ref1({ 2, 8, 5 });
+ std::vector<IFLTestValue> storage1(ref1.begin(), ref1.end());
+ IntrusiveForwardList<IFLTestValue> ifl;
+ ifl.assign(storage1.begin(), storage1.end());
+ ASSERT_LISTS_EQUAL(ref1, ifl);
+ std::forward_list<int> ref2({ 7, 1, 3 });
+ std::vector<IFLTestValue> storage2(ref2.begin(), ref2.end());
+ ifl.assign(storage2.begin(), storage2.end());
+ ASSERT_LISTS_EQUAL(ref2, ifl);
+}
+
+TEST(IntrusiveForwardList, PushPop) {
+ IFLTestValue value3(3);
+ IFLTestValue value7(7);
+ std::forward_list<int> ref;
+ IntrusiveForwardList<IFLTestValue> ifl;
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ ref.push_front(3);
+ ifl.push_front(value3);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ ASSERT_EQ(3, ifl.front());
+ ref.push_front(7);
+ ifl.push_front(value7);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ ASSERT_EQ(7, ifl.front());
+ ref.pop_front();
+ ifl.pop_front();
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ ASSERT_EQ(3, ifl.front());
+ ref.pop_front();
+ ifl.pop_front();
+ ASSERT_LISTS_EQUAL(ref, ifl);
+}
+
+TEST(IntrusiveForwardList, InsertAfter1) {
+ IFLTestValue value4(4);
+ IFLTestValue value8(8);
+ IFLTestValue value5(5);
+ IFLTestValue value3(3);
+ std::forward_list<int> ref;
+ IntrusiveForwardList<IFLTestValue> ifl;
+
+ auto ref_it = ref.insert_after(ref.before_begin(), 4);
+ auto ifl_it = ifl.insert_after(ifl.before_begin(), value4);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ ASSERT_EQ(*ref_it, *ifl_it);
+ CHECK(ref_it == ref.begin());
+ ASSERT_TRUE(ifl_it == ifl.begin());
+
+ ref_it = ref.insert_after(ref.begin(), 8);
+ ifl_it = ifl.insert_after(ifl.begin(), value8);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ ASSERT_EQ(*ref_it, *ifl_it);
+ CHECK(ref_it != ref.end());
+ ASSERT_TRUE(ifl_it != ifl.end());
+ CHECK(++ref_it == ref.end());
+ ASSERT_TRUE(++ifl_it == ifl.end());
+
+ ref_it = ref.insert_after(ref.begin(), 5);
+ ifl_it = ifl.insert_after(ifl.begin(), value5);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ ASSERT_EQ(*ref_it, *ifl_it);
+
+ ref_it = ref.insert_after(ref_it, 3);
+ ifl_it = ifl.insert_after(ifl_it, value3);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ ASSERT_EQ(*ref_it, *ifl_it);
+}
+
+TEST(IntrusiveForwardList, InsertAfter2) {
+ std::forward_list<int> ref;
+ IntrusiveForwardList<IFLTestValue> ifl;
+
+ auto ref_it = ref.insert_after(ref.before_begin(), { 2, 8, 5 });
+ std::vector<IFLTestValue> storage1({ { 2 }, { 8 }, { 5 } });
+ auto ifl_it = ifl.insert_after(ifl.before_begin(), storage1.begin(), storage1.end());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ ASSERT_EQ(*ref_it, *ifl_it);
+
+ std::vector<IFLTestValue> storage2({ { 7 }, { 2 } });
+ ref_it = ref.insert_after(ref.begin(), { 7, 2 });
+ ifl_it = ifl.insert_after(ifl.begin(), storage2.begin(), storage2.end());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ ASSERT_EQ(*ref_it, *ifl_it);
+
+ std::vector<IFLTestValue> storage3({ { 1 }, { 3 }, { 4 }, { 9 } });
+ ref_it = ref.begin();
+ ifl_it = ifl.begin();
+ std::advance(ref_it, std::distance(ref.begin(), ref.end()) - 1);
+ std::advance(ifl_it, std::distance(ifl.begin(), ifl.end()) - 1);
+ ref_it = ref.insert_after(ref_it, { 1, 3, 4, 9 });
+ ifl_it = ifl.insert_after(ifl_it, storage3.begin(), storage3.end());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+}
+
+TEST(IntrusiveForwardList, EraseAfter1) {
+ std::forward_list<int> ref({ 1, 2, 7, 4, 5 });
+ std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+ IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ CHECK_EQ(std::distance(ref.begin(), ref.end()), 5);
+
+ auto ref_it = ref.begin();
+ auto ifl_it = ifl.begin();
+ std::advance(ref_it, 2);
+ std::advance(ifl_it, 2);
+ ref_it = ref.erase_after(ref_it);
+ ifl_it = ifl.erase_after(ifl_it);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ CHECK_EQ(std::distance(ref.begin(), ref.end()), 4);
+ CHECK(ref_it != ref.end());
+ ASSERT_TRUE(ifl_it != ifl.end());
+ CHECK(++ref_it == ref.end());
+ ASSERT_TRUE(++ifl_it == ifl.end());
+
+ ref_it = ref.begin();
+ ifl_it = ifl.begin();
+ std::advance(ref_it, 2);
+ std::advance(ifl_it, 2);
+ ref_it = ref.erase_after(ref_it);
+ ifl_it = ifl.erase_after(ifl_it);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ CHECK_EQ(std::distance(ref.begin(), ref.end()), 3);
+ CHECK(ref_it == ref.end());
+ ASSERT_TRUE(ifl_it == ifl.end());
+
+ ref_it = ref.erase_after(ref.begin());
+ ifl_it = ifl.erase_after(ifl.begin());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ CHECK_EQ(std::distance(ref.begin(), ref.end()), 2);
+ CHECK(ref_it != ref.end());
+ ASSERT_TRUE(ifl_it != ifl.end());
+ CHECK(++ref_it == ref.end());
+ ASSERT_TRUE(++ifl_it == ifl.end());
+
+ ref_it = ref.erase_after(ref.before_begin());
+ ifl_it = ifl.erase_after(ifl.before_begin());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ CHECK_EQ(std::distance(ref.begin(), ref.end()), 1);
+ CHECK(ref_it == ref.begin());
+ ASSERT_TRUE(ifl_it == ifl.begin());
+
+ ref_it = ref.erase_after(ref.before_begin());
+ ifl_it = ifl.erase_after(ifl.before_begin());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ CHECK_EQ(std::distance(ref.begin(), ref.end()), 0);
+ CHECK(ref_it == ref.begin());
+ ASSERT_TRUE(ifl_it == ifl.begin());
+}
+
+TEST(IntrusiveForwardList, EraseAfter2) {
+ std::forward_list<int> ref({ 1, 2, 7, 4, 5, 3, 2, 8, 9 });
+ std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+ IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ CHECK_EQ(std::distance(ref.begin(), ref.end()), 9);
+
+ auto ref_it = ref.begin();
+ auto ifl_it = ifl.begin();
+ std::advance(ref_it, 3);
+ std::advance(ifl_it, 3);
+ ref_it = ref.erase_after(ref.begin(), ref_it);
+ ifl_it = ifl.erase_after(ifl.begin(), ifl_it);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ ASSERT_EQ(std::distance(ref.begin(), ref_it), std::distance(ifl.begin(), ifl_it));
+ CHECK_EQ(std::distance(ref.begin(), ref.end()), 7);
+
+ ref_it = ref.erase_after(ref_it, ref.end());
+ ifl_it = ifl.erase_after(ifl_it, ifl.end());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ CHECK(ref_it == ref.end());
+ ASSERT_TRUE(ifl_it == ifl.end());
+ CHECK_EQ(std::distance(ref.begin(), ref.end()), 2);
+
+ ref_it = ref.erase_after(ref.before_begin(), ref.end());
+ ifl_it = ifl.erase_after(ifl.before_begin(), ifl.end());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ CHECK(ref_it == ref.end());
+ ASSERT_TRUE(ifl_it == ifl.end());
+ CHECK_EQ(std::distance(ref.begin(), ref.end()), 0);
+}
+
+TEST(IntrusiveForwardList, SwapClear) {
+ std::forward_list<int> ref1({ 1, 2, 7 });
+ std::vector<IFLTestValue> storage1(ref1.begin(), ref1.end());
+ IntrusiveForwardList<IFLTestValue> ifl1(storage1.begin(), storage1.end());
+ std::forward_list<int> ref2({ 3, 8, 6 });
+ std::vector<IFLTestValue> storage2(ref2.begin(), ref2.end());
+ IntrusiveForwardList<IFLTestValue> ifl2(storage2.begin(), storage2.end());
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+ ref1.swap(ref2);
+ ifl1.swap(ifl2);
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+ ref1.clear();
+ ifl1.clear();
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+ swap(ref1, ref2);
+ swap(ifl1, ifl2);
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+ ref1.clear();
+ ifl1.clear();
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+}
+
+TEST(IntrusiveForwardList, SpliceAfter) {
+ std::forward_list<int> ref1({ 3, 1, 2, 7, 4, 5, 4, 8, 7 });
+ std::forward_list<int> ref2;
+ std::vector<IFLTestValue> storage(ref1.begin(), ref1.end());
+ IntrusiveForwardList<IFLTestValue> ifl1(storage.begin(), storage.end());
+ IntrusiveForwardList<IFLTestValue> ifl2;
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+ // Move everything to ref2/ifl2.
+ ref2.splice_after(ref2.before_begin(), ref1);
+ ifl2.splice_after(ifl2.before_begin(), ifl1);
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+ // Move first element (3) to ref1/ifl1.
+ ref1.splice_after(ref1.before_begin(), ref2, ref2.before_begin());
+ ifl1.splice_after(ifl1.before_begin(), ifl2, ifl2.before_begin());
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+ // Move second element (2) to ref1/ifl1 after the first element (3).
+ ref1.splice_after(ref1.begin(), ref2, ref2.begin());
+ ifl1.splice_after(ifl1.begin(), ifl2, ifl2.begin());
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+ // Move everything from ref2/ifl2 between the 2 elements now in ref1/ifl1.
+ ref1.splice_after(ref1.begin(), ref2);
+ ifl1.splice_after(ifl1.begin(), ifl2);
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+ std::forward_list<int> check({ 3, 1, 7, 4, 5, 4, 8, 7, 2 });
+ ASSERT_LISTS_EQUAL(check, ifl1);
+ ASSERT_TRUE(ifl2.empty());
+
+ // Empty splice_after().
+ ref2.splice_after(
+ ref2.before_begin(), ref1, ref1.before_begin(), ref1.begin());
+ ifl2.splice_after(ifl2.before_begin(), ifl1, ifl1.before_begin(), ifl1.begin());
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+ // Move { 1, 7 } to ref2/ifl2.
+ auto ref_it = ref1.begin();
+ auto ifl_it = ifl1.begin();
+ std::advance(ref_it, 3);
+ std::advance(ifl_it, 3);
+ ref2.splice_after(ref2.before_begin(), ref1, ref1.begin(), ref_it);
+ ifl2.splice_after(ifl2.before_begin(), ifl1, ifl1.begin(), ifl_it);
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+ // Move { 8, 7, 2 } to the beginning of ref1/ifl1.
+ ref_it = ref1.begin();
+ ifl_it = ifl1.begin();
+ std::advance(ref_it, 3);
+ std::advance(ifl_it, 3);
+ ref1.splice_after(ref1.before_begin(), ref1, ref_it, ref1.end());
+ ifl1.splice_after(ifl1.before_begin(), ifl1, ifl_it, ifl1.end());
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+
+ check.assign({ 8, 7, 2, 3, 4, 5, 4 });
+ ASSERT_LISTS_EQUAL(check, ifl1);
+ check.assign({ 1, 7 });
+ ASSERT_LISTS_EQUAL(check, ifl2);
+
+ // Move all but the first element to ref2/ifl2.
+ ref_it = ref2.begin();
+ ifl_it = ifl2.begin();
+ std::advance(ref_it, 1);
+ std::advance(ifl_it, 1);
+ ref2.splice_after(ref_it, ref1, ref1.begin(), ref1.end());
+ ifl2.splice_after(ifl_it, ifl1, ifl1.begin(), ifl1.end());
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+
+ check.assign({8});
+ ASSERT_LISTS_EQUAL(check, ifl1);
+
+ // Move the first element of ref1/ifl1 to the beginning of ref1/ifl1 (do nothing).
+ ref1.splice_after(ref1.before_begin(), ref1, ref1.before_begin());
+ ifl1.splice_after(ifl1.before_begin(), ifl1, ifl1.before_begin());
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(check, ifl1);
+
+ // Move the first element of ref2/ifl2 after itself (do nothing).
+ ref1.splice_after(ref1.begin(), ref1, ref1.before_begin());
+ ifl1.splice_after(ifl1.begin(), ifl1, ifl1.before_begin());
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(check, ifl1);
+
+ check.assign({ 1, 7, 7, 2, 3, 4, 5, 4 });
+ ASSERT_LISTS_EQUAL(check, ifl2);
+
+ // Move the first element of ref2/ifl2 to the beginning of ref2/ifl2 (do nothing).
+ ref2.splice_after(ref2.before_begin(), ref2, ref2.before_begin());
+ ifl2.splice_after(ifl2.before_begin(), ifl2, ifl2.before_begin());
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+ ASSERT_LISTS_EQUAL(check, ifl2);
+
+ // Move the first element of ref2/ifl2 after itself (do nothing).
+ ref2.splice_after(ref2.begin(), ref2, ref2.before_begin());
+ ifl2.splice_after(ifl2.begin(), ifl2, ifl2.before_begin());
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+ ASSERT_LISTS_EQUAL(check, ifl2);
+}
+
+TEST(IntrusiveForwardList, Remove) {
+ std::forward_list<int> ref({ 3, 1, 2, 7, 4, 5, 4, 8, 7 });
+ std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+ IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ ref.remove(1);
+ ifl.remove(1);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ ref.remove(4);
+ ifl.remove(4);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ auto odd = [](IFLTestValue value) { return (value.value & 1) != 0; }; // NOLINT(readability/braces)
+ ref.remove_if(odd);
+ ifl.remove_if(odd);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ auto all = [](IFLTestValue value ATTRIBUTE_UNUSED) { return true; }; // NOLINT(readability/braces)
+ ref.remove_if(all);
+ ifl.remove_if(all);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+}
+
+TEST(IntrusiveForwardList, Unique) {
+ std::forward_list<int> ref({ 3, 1, 1, 2, 3, 3, 7, 7, 4, 4, 5, 7 });
+ std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+ IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ ref.unique();
+ ifl.unique();
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ std::forward_list<int> check({ 3, 1, 2, 3, 7, 4, 5, 7 });
+ ASSERT_LISTS_EQUAL(check, ifl);
+
+ auto bin_pred = [](IFLTestValue lhs, IFLTestValue rhs) {
+ return (lhs.value & ~1) == (rhs.value & ~1);
+ };
+ ref.unique(bin_pred);
+ ifl.unique(bin_pred);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ check.assign({ 3, 1, 2, 7, 4, 7 });
+ ASSERT_LISTS_EQUAL(check, ifl);
+}
+
+TEST(IntrusiveForwardList, Merge) {
+ std::forward_list<int> ref1({ 1, 4, 8, 8, 12 });
+ std::vector<IFLTestValue> storage1(ref1.begin(), ref1.end());
+ IntrusiveForwardList<IFLTestValue> ifl1(storage1.begin(), storage1.end());
+ std::forward_list<int> ref2({ 3, 5, 6, 7, 9 });
+ std::vector<IFLTestValue> storage2(ref2.begin(), ref2.end());
+ IntrusiveForwardList<IFLTestValue> ifl2(storage2.begin(), storage2.end());
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+ CHECK(std::is_sorted(ref1.begin(), ref1.end()));
+ CHECK(std::is_sorted(ref2.begin(), ref2.end()));
+ ref1.merge(ref2);
+ ifl1.merge(ifl2);
+ ASSERT_LISTS_EQUAL(ref1, ifl1);
+ ASSERT_LISTS_EQUAL(ref2, ifl2);
+ CHECK(ref2.empty());
+ std::forward_list<int> check({ 1, 3, 4, 5, 6, 7, 8, 8, 9, 12 });
+ ASSERT_LISTS_EQUAL(check, ifl1);
+}
+
+TEST(IntrusiveForwardList, Sort1) {
+ std::forward_list<int> ref({ 2, 9, 8, 3, 7, 4, 1, 5, 3, 0 });
+ std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+ IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ CHECK(!std::is_sorted(ref.begin(), ref.end()));
+ ref.sort();
+ ifl.sort();
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ std::forward_list<int> check({ 0, 1, 2, 3, 3, 4, 5, 7, 8, 9 });
+ ASSERT_LISTS_EQUAL(check, ifl);
+}
+
+TEST(IntrusiveForwardList, Sort2) {
+ std::forward_list<int> ref({ 2, 9, 8, 3, 7, 4, 1, 5, 3, 0 });
+ std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+ IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ auto cmp = [](IFLTestValue lhs, IFLTestValue rhs) {
+ return (lhs.value & ~1) < (rhs.value & ~1);
+ };
+ CHECK(!std::is_sorted(ref.begin(), ref.end(), cmp));
+ ref.sort(cmp);
+ ifl.sort(cmp);
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ std::forward_list<int> check({ 1, 0, 2, 3, 3, 4, 5, 7, 9, 8 });
+ ASSERT_LISTS_EQUAL(check, ifl);
+}
+
+TEST(IntrusiveForwardList, Reverse) {
+ std::forward_list<int> ref({ 8, 3, 5, 4, 1, 3 });
+ std::vector<IFLTestValue> storage(ref.begin(), ref.end());
+ IntrusiveForwardList<IFLTestValue> ifl(storage.begin(), storage.end());
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ CHECK(!std::is_sorted(ref.begin(), ref.end()));
+ ref.reverse();
+ ifl.reverse();
+ ASSERT_LISTS_EQUAL(ref, ifl);
+ std::forward_list<int> check({ 3, 1, 4, 5, 3, 8 });
+ ASSERT_LISTS_EQUAL(check, ifl);
+}
+
+} // namespace art
diff --git a/runtime/arch/x86/thread_x86.cc b/runtime/arch/x86/thread_x86.cc
index 3d19f06..c39d122 100644
--- a/runtime/arch/x86/thread_x86.cc
+++ b/runtime/arch/x86/thread_x86.cc
@@ -45,16 +45,17 @@
MutexLock mu(nullptr, *Locks::modify_ldt_lock_);
const uintptr_t base = reinterpret_cast<uintptr_t>(this);
- const size_t limit = kPageSize;
+ const size_t limit = sizeof(Thread);
const int contents = MODIFY_LDT_CONTENTS_DATA;
const int seg_32bit = 1;
const int read_exec_only = 0;
- const int limit_in_pages = 0;
+ const int limit_in_pages = 1;
const int seg_not_present = 0;
const int useable = 1;
- int entry_number = -1;
+ int entry_number;
+ uint16_t table_indicator;
#if defined(__APPLE__)
descriptor_table_entry_t entry;
@@ -77,41 +78,52 @@
if (entry_number == -1) {
PLOG(FATAL) << "i386_set_ldt failed";
}
+
+ table_indicator = 1 << 2; // LDT
#else
- // Read current LDT entries.
- static_assert(static_cast<size_t>(LDT_ENTRY_SIZE) == sizeof(uint64_t),
- "LDT_ENTRY_SIZE is different from sizeof(uint64_t).");
- std::vector<uint64_t> ldt(LDT_ENTRIES);
- size_t ldt_size(sizeof(uint64_t) * ldt.size());
- memset(&ldt[0], 0, ldt_size);
- // TODO: why doesn't this return LDT_ENTRY_SIZE * LDT_ENTRIES for the main thread?
- syscall(__NR_modify_ldt, 0, &ldt[0], ldt_size);
+ // We use a GDT entry on Linux.
+ user_desc gdt_entry;
+ memset(&gdt_entry, 0, sizeof(gdt_entry));
- // Find the first empty slot.
- for (entry_number = 0; entry_number < LDT_ENTRIES && ldt[entry_number] != 0; ++entry_number) {
- }
- if (entry_number >= LDT_ENTRIES) {
- LOG(FATAL) << "Failed to find a free LDT slot";
- }
+ // On Linux, there are 3 TLS GDT entries. We use one of those to to store our segment descriptor
+ // data.
+ //
+ // This entry must be shared, as the kernel only guarantees three TLS entries. For simplicity
+ // (and locality), use this local global, which practically becomes readonly after the first
+ // (startup) thread of the runtime has been initialized (during Runtime::Start()).
+ //
+ // We also share this between all runtimes in the process. This is both for simplicity (one
+ // well-known slot) as well as to avoid the three-slot limitation. Downside is that we cannot
+ // free the slot when it is known that a runtime stops.
+ static unsigned int gdt_entry_number = -1;
- // Update LDT entry.
- user_desc ldt_entry;
- memset(&ldt_entry, 0, sizeof(ldt_entry));
- ldt_entry.entry_number = entry_number;
- ldt_entry.base_addr = base;
- ldt_entry.limit = limit;
- ldt_entry.seg_32bit = seg_32bit;
- ldt_entry.contents = contents;
- ldt_entry.read_exec_only = read_exec_only;
- ldt_entry.limit_in_pages = limit_in_pages;
- ldt_entry.seg_not_present = seg_not_present;
- ldt_entry.useable = useable;
- CHECK_EQ(0, syscall(__NR_modify_ldt, 1, &ldt_entry, sizeof(ldt_entry)));
- entry_number = ldt_entry.entry_number;
+ if (gdt_entry_number == static_cast<unsigned int>(-1)) {
+ gdt_entry.entry_number = -1; // Let the kernel choose.
+ } else {
+ gdt_entry.entry_number = gdt_entry_number;
+ }
+ gdt_entry.base_addr = base;
+ gdt_entry.limit = limit;
+ gdt_entry.seg_32bit = seg_32bit;
+ gdt_entry.contents = contents;
+ gdt_entry.read_exec_only = read_exec_only;
+ gdt_entry.limit_in_pages = limit_in_pages;
+ gdt_entry.seg_not_present = seg_not_present;
+ gdt_entry.useable = useable;
+ int rc = syscall(__NR_set_thread_area, &gdt_entry);
+ if (rc != -1) {
+ entry_number = gdt_entry.entry_number;
+ if (gdt_entry_number == static_cast<unsigned int>(-1)) {
+ gdt_entry_number = entry_number; // Save the kernel-assigned entry number.
+ }
+ } else {
+ PLOG(FATAL) << "set_thread_area failed";
+ UNREACHABLE();
+ }
+ table_indicator = 0; // GDT
#endif
- // Change %fs to be new LDT entry.
- uint16_t table_indicator = 1 << 2; // LDT
+ // Change %fs to be new DT entry.
uint16_t rpl = 3; // Requested privilege level
uint16_t selector = (entry_number << 3) | table_indicator | rpl;
__asm__ __volatile__("movw %w0, %%fs"
@@ -163,13 +175,18 @@
UNUSED(selector);
// i386_set_ldt(selector >> 3, 0, 1);
#else
- user_desc ldt_entry;
- memset(&ldt_entry, 0, sizeof(ldt_entry));
- ldt_entry.entry_number = selector >> 3;
- ldt_entry.contents = MODIFY_LDT_CONTENTS_DATA;
- ldt_entry.seg_not_present = 1;
-
- syscall(__NR_modify_ldt, 1, &ldt_entry, sizeof(ldt_entry));
+ // Note if we wanted to clean up the GDT entry, we would do that here, when the *last* thread
+ // is being deleted. But see the comment on gdt_entry_number. Code would look like this:
+ //
+ // user_desc gdt_entry;
+ // memset(&gdt_entry, 0, sizeof(gdt_entry));
+ // gdt_entry.entry_number = selector >> 3;
+ // gdt_entry.contents = MODIFY_LDT_CONTENTS_DATA;
+ // // "Empty" = Delete = seg_not_present==1 && read_exec_only==1.
+ // gdt_entry.seg_not_present = 1;
+ // gdt_entry.read_exec_only = 1;
+ // syscall(__NR_set_thread_area, &gdt_entry);
+ UNUSED(selector);
#endif
}
diff --git a/runtime/base/macros.h b/runtime/base/macros.h
index dc692d2..7a293c7 100644
--- a/runtime/base/macros.h
+++ b/runtime/base/macros.h
@@ -138,10 +138,10 @@
#define SIZEOF_MEMBER(t, f) sizeof((reinterpret_cast<t*>(4096))->f)
#define OFFSETOF_MEMBER(t, f) \
- (reinterpret_cast<const char*>(&reinterpret_cast<t*>(16)->f) - reinterpret_cast<const char*>(16)) // NOLINT
+ (reinterpret_cast<uintptr_t>(&reinterpret_cast<t*>(16)->f) - static_cast<uintptr_t>(16u)) // NOLINT
-#define OFFSETOF_VOLATILE_MEMBER(t, f) \
- (reinterpret_cast<volatile char*>(&reinterpret_cast<t*>(16)->f) - reinterpret_cast<volatile char*>(16)) // NOLINT
+#define OFFSETOF_MEMBERPTR(t, f) \
+ (reinterpret_cast<uintptr_t>(&(reinterpret_cast<t*>(16)->*f)) - static_cast<uintptr_t>(16)) // NOLINT
#define PACKED(x) __attribute__ ((__aligned__(x), __packed__))
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 17e0339..3dca12a 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -76,7 +76,6 @@
kReferenceQueueClearedReferencesLock,
kReferenceProcessorLock,
kJitDebugInterfaceLock,
- kJitCodeCacheLock,
kAllocSpaceLock,
kBumpPointerSpaceBlockLock,
kArenaPoolLock,
@@ -89,6 +88,7 @@
kTracingUniqueMethodsLock,
kTracingStreamingLock,
kDeoptimizedMethodsLock,
+ kJitCodeCacheLock,
kClassLoaderClassesLock,
kDefaultMutexLevel,
kMarkSweepLargeObjectLock,
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index d832552..55f68d3 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -2362,6 +2362,10 @@
}
void Dbg::SuspendVM() {
+ // Avoid a deadlock between GC and debugger where GC gets suspended during GC. b/25800335.
+ gc::ScopedGCCriticalSection gcs(Thread::Current(),
+ gc::kGcCauseDebugger,
+ gc::kCollectorTypeDebugger);
Runtime::Current()->GetThreadList()->SuspendAllForDebugger();
}
@@ -4101,6 +4105,8 @@
// Suspend other threads if the invoke is not single-threaded.
if ((pReq->options & JDWP::INVOKE_SINGLE_THREADED) == 0) {
ScopedThreadSuspension sts(soa.Self(), kWaitingForDebuggerSuspension);
+ // Avoid a deadlock between GC and debugger where GC gets suspended during GC. b/25800335.
+ gc::ScopedGCCriticalSection gcs(soa.Self(), gc::kGcCauseDebugger, gc::kCollectorTypeDebugger);
VLOG(jdwp) << " Suspending all threads";
Runtime::Current()->GetThreadList()->SuspendAllForDebugger();
}
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 5c5abeb..9f073a6 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -147,6 +147,10 @@
}
bool FaultManager::HandleFaultByOtherHandlers(int sig, siginfo_t* info, void* context) {
+ if (other_handlers_.empty()) {
+ return false;
+ }
+
Thread* self = Thread::Current();
DCHECK(self != nullptr);
diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h
index ae41226..4ffc8af 100644
--- a/runtime/gc/collector_type.h
+++ b/runtime/gc/collector_type.h
@@ -44,6 +44,8 @@
kCollectorTypeInstrumentation,
// Fake collector for adding or removing application image spaces.
kCollectorTypeAddRemoveAppImageSpace,
+ // Fake collector used to implement exclusion between GC and debugger.
+ kCollectorTypeDebugger,
// A homogeneous space compaction collector used in background transition
// when both foreground and background collector are CMS.
kCollectorTypeHomogeneousSpaceCompact,
diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc
index 679432b..18e5703 100644
--- a/runtime/gc/gc_cause.cc
+++ b/runtime/gc/gc_cause.cc
@@ -35,6 +35,7 @@
case kGcCauseTrim: return "HeapTrim";
case kGcCauseInstrumentation: return "Instrumentation";
case kGcCauseAddRemoveAppImageSpace: return "AddRemoveAppImageSpace";
+ case kGcCauseDebugger: return "Debugger";
default:
LOG(FATAL) << "Unreachable";
UNREACHABLE();
diff --git a/runtime/gc/gc_cause.h b/runtime/gc/gc_cause.h
index c6b505c..ad67eb7 100644
--- a/runtime/gc/gc_cause.h
+++ b/runtime/gc/gc_cause.h
@@ -43,6 +43,8 @@
kGcCauseInstrumentation,
// Not a real GC cause, used to add or remove app image spaces.
kGcCauseAddRemoveAppImageSpace,
+ // Not a real GC cause, used to implement exclusion between GC and debugger.
+ kGcCauseDebugger,
// GC triggered for background transition when both foreground and background collector are CMS.
kGcCauseHomogeneousSpaceCompact,
};
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index a432782..97dbe5d 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -292,7 +292,7 @@
// Pop the shadow frame before calling into compiled code.
self->PopShadowFrame();
- ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result);
+ ArtInterpreterToCompiledCodeBridge(self, nullptr, code_item, &shadow_frame, &result);
// Push the shadow frame back as the caller will expect it.
self->PushShadowFrame(&shadow_frame);
@@ -535,6 +535,10 @@
return JValue();
}
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ jit->NotifyCompiledCodeToInterpreterTransition(self, shadow_frame->GetMethod());
+ }
return Execute(self, code_item, *shadow_frame, JValue());
}
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 3453abc..12d70c5 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -503,6 +503,7 @@
uint32_t vregC) ALWAYS_INLINE;
void ArtInterpreterToCompiledCodeBridge(Thread* self,
+ ArtMethod* caller,
const DexFile::CodeItem* code_item,
ShadowFrame* shadow_frame,
JValue* result)
@@ -530,6 +531,10 @@
uint16_t arg_offset = (code_item == nullptr)
? 0
: code_item->registers_size_ - code_item->ins_size_;
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr && caller != nullptr) {
+ jit->NotifyInterpreterToCompiledCodeTransition(self, caller);
+ }
method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset),
(shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t),
result, method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty());
@@ -726,7 +731,8 @@
target->GetEntryPointFromQuickCompiledCode())) {
ArtInterpreterToInterpreterBridge(self, code_item, new_shadow_frame, result);
} else {
- ArtInterpreterToCompiledCodeBridge(self, code_item, new_shadow_frame, result);
+ ArtInterpreterToCompiledCodeBridge(
+ self, shadow_frame.GetMethod(), code_item, new_shadow_frame, result);
}
} else {
UnstartedRuntime::Invoke(self, code_item, new_shadow_frame, result, first_dest_reg);
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index fb98175..e5b89e2 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -635,7 +635,7 @@
jit->InvokeVirtualOrInterface(
self, receiver, sf_method, shadow_frame.GetDexPC(), called_method);
}
- jit->AddSamples(self, sf_method, 1);
+ jit->AddSamples(self, sf_method, 1, /*with_backedges*/false);
}
// TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT.
if (type == kVirtual || type == kInterface) {
@@ -681,7 +681,7 @@
if (jit != nullptr) {
jit->InvokeVirtualOrInterface(
self, receiver, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method);
- jit->AddSamples(self, shadow_frame.GetMethod(), 1);
+ jit->AddSamples(self, shadow_frame.GetMethod(), 1, /*with_backedges*/false);
}
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
// TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT.
@@ -1001,8 +1001,11 @@
return branch_offset <= 0;
}
-void ArtInterpreterToCompiledCodeBridge(Thread* self, const DexFile::CodeItem* code_item,
- ShadowFrame* shadow_frame, JValue* result);
+void ArtInterpreterToCompiledCodeBridge(Thread* self,
+ ArtMethod* caller,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame* shadow_frame,
+ JValue* result);
// Explicitly instantiate all DoInvoke functions.
#define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range, _do_check) \
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index c95af6f..13cfb98 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -78,7 +78,7 @@
#define HOTNESS_UPDATE() \
do { \
if (jit != nullptr) { \
- jit->AddSamples(self, method, 1); \
+ jit->AddSamples(self, method, 1, /*with_backedges*/ true); \
} \
} while (false)
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index ca1d635..4323d4f 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -89,7 +89,7 @@
#define HOTNESS_UPDATE() \
do { \
if (jit != nullptr) { \
- jit->AddSamples(self, method, 1); \
+ jit->AddSamples(self, method, 1, /*with_backedges*/ true); \
} \
} while (false)
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index e005589..bd1af04 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -689,7 +689,7 @@
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr) {
int16_t count = shadow_frame->GetCachedHotnessCountdown() - shadow_frame->GetHotnessCountdown();
- jit->AddSamples(self, method, count);
+ jit->AddSamples(self, method, count, /*with_backedges*/ true);
}
return MterpSetUpHotnessCountdown(method, shadow_frame);
}
@@ -702,7 +702,7 @@
uint32_t dex_pc = shadow_frame->GetDexPC();
jit::Jit* jit = Runtime::Current()->GetJit();
if ((jit != nullptr) && (offset <= 0)) {
- jit->AddSamples(self, method, 1);
+ jit->AddSamples(self, method, 1, /*with_backedges*/ true);
}
int16_t countdown_value = MterpSetUpHotnessCountdown(method, shadow_frame);
if (countdown_value == jit::kJitCheckForOSR) {
@@ -722,7 +722,7 @@
jit::Jit* jit = Runtime::Current()->GetJit();
if (offset <= 0) {
// Keep updating hotness in case a compilation request was dropped. Eventually it will retry.
- jit->AddSamples(self, method, 1);
+ jit->AddSamples(self, method, 1, /*with_backedges*/ true);
}
// Assumes caller has already determined that an OSR check is appropriate.
return jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, result);
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index fe388cd..1f473e4 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -16,11 +16,13 @@
#include "unstarted_runtime.h"
+#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <cmath>
#include <limits>
+#include <locale>
#include <unordered_map>
#include "ScopedLocalRef.h"
@@ -42,6 +44,7 @@
#include "mirror/object_array-inl.h"
#include "mirror/string-inl.h"
#include "nth_caller_visitor.h"
+#include "reflection.h"
#include "thread.h"
#include "transaction.h"
#include "well_known_classes.h"
@@ -70,6 +73,43 @@
}
}
+// Restricted support for character upper case / lower case. Only support ASCII, where
+// it's easy. Abort the transaction otherwise.
+static void CharacterLowerUpper(Thread* self,
+ ShadowFrame* shadow_frame,
+ JValue* result,
+ size_t arg_offset,
+ bool to_lower_case) SHARED_REQUIRES(Locks::mutator_lock_) {
+ uint32_t int_value = static_cast<uint32_t>(shadow_frame->GetVReg(arg_offset));
+
+ // Only ASCII (7-bit).
+ if (!isascii(int_value)) {
+ AbortTransactionOrFail(self,
+ "Only support ASCII characters for toLowerCase/toUpperCase: %u",
+ int_value);
+ return;
+ }
+
+ std::locale c_locale("C");
+ char char_value = static_cast<char>(int_value);
+
+ if (to_lower_case) {
+ result->SetI(std::tolower(char_value, c_locale));
+ } else {
+ result->SetI(std::toupper(char_value, c_locale));
+ }
+}
+
+void UnstartedRuntime::UnstartedCharacterToLowerCase(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ CharacterLowerUpper(self, shadow_frame, result, arg_offset, true);
+}
+
+void UnstartedRuntime::UnstartedCharacterToUpperCase(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ CharacterLowerUpper(self, shadow_frame, result, arg_offset, false);
+}
+
// Helper function to deal with class loading in an unstarted runtime.
static void UnstartedRuntimeFindClass(Thread* self, Handle<mirror::String> className,
Handle<mirror::ClassLoader> class_loader, JValue* result,
@@ -313,6 +353,171 @@
result->SetL(klass->GetDexFile().GetEnclosingClass(klass));
}
+void UnstartedRuntime::UnstartedClassGetInnerClassFlags(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> klass(hs.NewHandle(
+ reinterpret_cast<mirror::Class*>(shadow_frame->GetVRegReference(arg_offset))));
+ const int32_t default_value = shadow_frame->GetVReg(arg_offset + 1);
+ result->SetI(mirror::Class::GetInnerClassFlags(klass, default_value));
+}
+
+static std::unique_ptr<MemMap> FindAndExtractEntry(const std::string& jar_file,
+ const char* entry_name,
+ size_t* size,
+ std::string* error_msg) {
+ CHECK(size != nullptr);
+
+ std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(jar_file.c_str(), error_msg));
+ if (zip_archive == nullptr) {
+ return nullptr;;
+ }
+ std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(entry_name, error_msg));
+ if (zip_entry == nullptr) {
+ return nullptr;
+ }
+ std::unique_ptr<MemMap> tmp_map(
+ zip_entry->ExtractToMemMap(jar_file.c_str(), entry_name, error_msg));
+ if (tmp_map == nullptr) {
+ return nullptr;
+ }
+
+ // OK, from here everything seems fine.
+ *size = zip_entry->GetUncompressedLength();
+ return tmp_map;
+}
+
+static void GetResourceAsStream(Thread* self,
+ ShadowFrame* shadow_frame,
+ JValue* result,
+ size_t arg_offset) SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Object* resource_obj = shadow_frame->GetVRegReference(arg_offset + 1);
+ if (resource_obj == nullptr) {
+ AbortTransactionOrFail(self, "null name for getResourceAsStream");
+ return;
+ }
+ CHECK(resource_obj->IsString());
+ mirror::String* resource_name = resource_obj->AsString();
+
+ std::string resource_name_str = resource_name->ToModifiedUtf8();
+ if (resource_name_str.empty() || resource_name_str == "/") {
+ AbortTransactionOrFail(self,
+ "Unsupported name %s for getResourceAsStream",
+ resource_name_str.c_str());
+ return;
+ }
+ const char* resource_cstr = resource_name_str.c_str();
+ if (resource_cstr[0] == '/') {
+ resource_cstr++;
+ }
+
+ Runtime* runtime = Runtime::Current();
+
+ std::vector<std::string> split;
+ Split(runtime->GetBootClassPathString(), ':', &split);
+ if (split.empty()) {
+ AbortTransactionOrFail(self,
+ "Boot classpath not set or split error:: %s",
+ runtime->GetBootClassPathString().c_str());
+ return;
+ }
+
+ std::unique_ptr<MemMap> mem_map;
+ size_t map_size;
+ std::string last_error_msg; // Only store the last message (we could concatenate).
+
+ for (const std::string& jar_file : split) {
+ mem_map = FindAndExtractEntry(jar_file, resource_cstr, &map_size, &last_error_msg);
+ if (mem_map != nullptr) {
+ break;
+ }
+ }
+
+ if (mem_map == nullptr) {
+ // Didn't find it. There's a good chance this will be the same at runtime, but still
+ // conservatively abort the transaction here.
+ AbortTransactionOrFail(self,
+ "Could not find resource %s. Last error was %s.",
+ resource_name_str.c_str(),
+ last_error_msg.c_str());
+ return;
+ }
+
+ StackHandleScope<3> hs(self);
+
+ // Create byte array for content.
+ Handle<mirror::ByteArray> h_array(hs.NewHandle(mirror::ByteArray::Alloc(self, map_size)));
+ if (h_array.Get() == nullptr) {
+ AbortTransactionOrFail(self, "Could not find/create byte array class");
+ return;
+ }
+ // Copy in content.
+ memcpy(h_array->GetData(), mem_map->Begin(), map_size);
+ // Be proactive releasing memory.
+ mem_map.release();
+
+ // Create a ByteArrayInputStream.
+ Handle<mirror::Class> h_class(hs.NewHandle(
+ runtime->GetClassLinker()->FindClass(self,
+ "Ljava/io/ByteArrayInputStream;",
+ ScopedNullHandle<mirror::ClassLoader>())));
+ if (h_class.Get() == nullptr) {
+ AbortTransactionOrFail(self, "Could not find ByteArrayInputStream class");
+ return;
+ }
+ if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
+ AbortTransactionOrFail(self, "Could not initialize ByteArrayInputStream class");
+ return;
+ }
+
+ Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self)));
+ if (h_obj.Get() == nullptr) {
+ AbortTransactionOrFail(self, "Could not allocate ByteArrayInputStream object");
+ return;
+ }
+
+ auto* cl = Runtime::Current()->GetClassLinker();
+ ArtMethod* constructor = h_class->FindDeclaredDirectMethod(
+ "<init>", "([B)V", cl->GetImagePointerSize());
+ if (constructor == nullptr) {
+ AbortTransactionOrFail(self, "Could not find ByteArrayInputStream constructor");
+ return;
+ }
+
+ uint32_t args[1];
+ args[0] = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(h_array.Get()));
+ EnterInterpreterFromInvoke(self, constructor, h_obj.Get(), args, nullptr);
+
+ if (self->IsExceptionPending()) {
+ AbortTransactionOrFail(self, "Could not run ByteArrayInputStream constructor");
+ return;
+ }
+
+ result->SetL(h_obj.Get());
+}
+
+void UnstartedRuntime::UnstartedClassLoaderGetResourceAsStream(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ {
+ mirror::Object* this_obj = shadow_frame->GetVRegReference(arg_offset);
+ CHECK(this_obj != nullptr);
+ CHECK(this_obj->IsClassLoader());
+
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> this_classloader_class(hs.NewHandle(this_obj->GetClass()));
+
+ if (self->DecodeJObject(WellKnownClasses::java_lang_BootClassLoader) !=
+ this_classloader_class.Get()) {
+ AbortTransactionOrFail(self,
+ "Unsupported classloader type %s for getResourceAsStream",
+ PrettyClass(this_classloader_class.Get()).c_str());
+ return;
+ }
+ }
+
+ GetResourceAsStream(self, shadow_frame, result, arg_offset);
+}
+
void UnstartedRuntime::UnstartedVmClassLoaderFindLoadedClass(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
@@ -629,6 +834,22 @@
result->SetD(floor(shadow_frame->GetVRegDouble(arg_offset)));
}
+void UnstartedRuntime::UnstartedMathSin(
+ Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ result->SetD(sin(shadow_frame->GetVRegDouble(arg_offset)));
+}
+
+void UnstartedRuntime::UnstartedMathCos(
+ Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ result->SetD(cos(shadow_frame->GetVRegDouble(arg_offset)));
+}
+
+void UnstartedRuntime::UnstartedMathPow(
+ Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ result->SetD(pow(shadow_frame->GetVRegDouble(arg_offset),
+ shadow_frame->GetVRegDouble(arg_offset + 2)));
+}
+
void UnstartedRuntime::UnstartedObjectHashCode(
Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset);
@@ -805,112 +1026,6 @@
UnstartedMemoryPeekArray(Primitive::kPrimByte, self, shadow_frame, arg_offset);
}
-// This allows reading security.properties in an unstarted runtime and initialize Security.
-void UnstartedRuntime::UnstartedSecurityGetSecurityPropertiesReader(
- Thread* self, ShadowFrame* shadow_frame ATTRIBUTE_UNUSED, JValue* result,
- size_t arg_offset ATTRIBUTE_UNUSED) {
- Runtime* runtime = Runtime::Current();
-
- std::vector<std::string> split;
- Split(runtime->GetBootClassPathString(), ':', &split);
- if (split.empty()) {
- AbortTransactionOrFail(self,
- "Boot classpath not set or split error:: %s",
- runtime->GetBootClassPathString().c_str());
- return;
- }
- const std::string& source = split[0];
-
- mirror::String* string_data;
-
- // Use a block to enclose the I/O and MemMap code so buffers are released early.
- {
- std::string error_msg;
- std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(source.c_str(), &error_msg));
- if (zip_archive.get() == nullptr) {
- AbortTransactionOrFail(self,
- "Could not open zip file %s: %s",
- source.c_str(),
- error_msg.c_str());
- return;
- }
- std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find("java/security/security.properties",
- &error_msg));
- if (zip_entry.get() == nullptr) {
- AbortTransactionOrFail(self,
- "Could not find security.properties file in %s: %s",
- source.c_str(),
- error_msg.c_str());
- return;
- }
- std::unique_ptr<MemMap> map(zip_entry->ExtractToMemMap(source.c_str(),
- "java/security/security.properties",
- &error_msg));
- if (map.get() == nullptr) {
- AbortTransactionOrFail(self,
- "Could not unzip security.properties file in %s: %s",
- source.c_str(),
- error_msg.c_str());
- return;
- }
-
- uint32_t length = zip_entry->GetUncompressedLength();
- std::unique_ptr<char[]> tmp(new char[length + 1]);
- memcpy(tmp.get(), map->Begin(), length);
- tmp.get()[length] = 0; // null terminator
-
- string_data = mirror::String::AllocFromModifiedUtf8(self, tmp.get());
- }
-
- if (string_data == nullptr) {
- AbortTransactionOrFail(self, "Could not create string from file content of %s", source.c_str());
- return;
- }
-
- // Create a StringReader.
- StackHandleScope<3> hs(self);
- Handle<mirror::String> h_string(hs.NewHandle(string_data));
-
- Handle<mirror::Class> h_class(hs.NewHandle(
- runtime->GetClassLinker()->FindClass(self,
- "Ljava/io/StringReader;",
- ScopedNullHandle<mirror::ClassLoader>())));
- if (h_class.Get() == nullptr) {
- AbortTransactionOrFail(self, "Could not find StringReader class");
- return;
- }
-
- if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
- AbortTransactionOrFail(self, "Could not initialize StringReader class");
- return;
- }
-
- Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self)));
- if (h_obj.Get() == nullptr) {
- AbortTransactionOrFail(self, "Could not allocate StringReader object");
- return;
- }
-
- auto* cl = Runtime::Current()->GetClassLinker();
- ArtMethod* constructor = h_class->FindDeclaredDirectMethod(
- "<init>", "(Ljava/lang/String;)V", cl->GetImagePointerSize());
- if (constructor == nullptr) {
- AbortTransactionOrFail(self, "Could not find StringReader constructor");
- return;
- }
-
- uint32_t args[1];
- args[0] = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(h_string.Get()));
- EnterInterpreterFromInvoke(self, constructor, h_obj.Get(), args, nullptr);
-
- if (self->IsExceptionPending()) {
- AbortTransactionOrFail(self, "Could not run StringReader constructor");
- return;
- }
-
- result->SetL(h_obj.Get());
-}
-
// This allows reading the new style of String objects during compilation.
void UnstartedRuntime::UnstartedStringGetCharsNoCheck(
Thread* self, ShadowFrame* shadow_frame, JValue* result ATTRIBUTE_UNUSED, size_t arg_offset) {
@@ -1268,6 +1383,36 @@
result->SetJ(l);
}
+void UnstartedRuntime::UnstartedMethodInvoke(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ JNIEnvExt* env = self->GetJniEnv();
+ ScopedObjectAccessUnchecked soa(self);
+
+ mirror::Object* java_method_obj = shadow_frame->GetVRegReference(arg_offset);
+ ScopedLocalRef<jobject> java_method(env,
+ java_method_obj == nullptr ? nullptr :env->AddLocalReference<jobject>(java_method_obj));
+
+ mirror::Object* java_receiver_obj = shadow_frame->GetVRegReference(arg_offset + 1);
+ ScopedLocalRef<jobject> java_receiver(env,
+ java_receiver_obj == nullptr ? nullptr : env->AddLocalReference<jobject>(java_receiver_obj));
+
+ mirror::Object* java_args_obj = shadow_frame->GetVRegReference(arg_offset + 2);
+ ScopedLocalRef<jobject> java_args(env,
+ java_args_obj == nullptr ? nullptr : env->AddLocalReference<jobject>(java_args_obj));
+
+ ScopedLocalRef<jobject> result_jobj(env,
+ InvokeMethod(soa, java_method.get(), java_receiver.get(), java_args.get()));
+
+ result->SetL(self->DecodeJObject(result_jobj.get()));
+
+ // Conservatively flag all exceptions as transaction aborts. This way we don't need to unwrap
+ // InvocationTargetExceptions.
+ if (self->IsExceptionPending()) {
+ AbortTransactionOrFail(self, "Failed Method.invoke");
+ }
+}
+
void UnstartedRuntime::UnstartedJNIVMRuntimeNewUnpaddedArray(
Thread* self, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED,
@@ -1551,7 +1696,13 @@
if (iter != invoke_handlers_.end()) {
// Clear out the result in case it's not zeroed out.
result->SetL(0);
+
+ // Push the shadow frame. This is so the failing method can be seen in abort dumps.
+ self->PushShadowFrame(shadow_frame);
+
(*iter->second)(self, shadow_frame, result, arg_offset);
+
+ self->PopShadowFrame();
} else {
// Not special, continue with regular interpreter execution.
ArtInterpreterToInterpreterBridge(self, code_item, shadow_frame, result);
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index 3b5bee82..b8553b5 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -19,6 +19,8 @@
// Methods that intercept available libcore implementations.
#define UNSTARTED_RUNTIME_DIRECT_LIST(V) \
+ V(CharacterToLowerCase, "int java.lang.Character.toLowerCase(int)") \
+ V(CharacterToUpperCase, "int java.lang.Character.toUpperCase(int)") \
V(ClassForName, "java.lang.Class java.lang.Class.forName(java.lang.String)") \
V(ClassForNameLong, "java.lang.Class java.lang.Class.forName(java.lang.String, boolean, java.lang.ClassLoader)") \
V(ClassClassForName, "java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader)") \
@@ -27,6 +29,8 @@
V(ClassGetDeclaredMethod, "java.lang.reflect.Method java.lang.Class.getDeclaredMethodInternal(java.lang.String, java.lang.Class[])") \
V(ClassGetDeclaredConstructor, "java.lang.reflect.Constructor java.lang.Class.getDeclaredConstructorInternal(java.lang.Class[])") \
V(ClassGetEnclosingClass, "java.lang.Class java.lang.Class.getEnclosingClass()") \
+ V(ClassGetInnerClassFlags, "int java.lang.Class.getInnerClassFlags(int)") \
+ V(ClassLoaderGetResourceAsStream, "java.io.InputStream java.lang.ClassLoader.getResourceAsStream(java.lang.String)") \
V(VmClassLoaderFindLoadedClass, "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") \
V(VoidLookupType, "java.lang.Class java.lang.Void.lookupType()") \
V(SystemArraycopy, "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)") \
@@ -39,6 +43,9 @@
V(ThreadLocalGet, "java.lang.Object java.lang.ThreadLocal.get()") \
V(MathCeil, "double java.lang.Math.ceil(double)") \
V(MathFloor, "double java.lang.Math.floor(double)") \
+ V(MathSin, "double java.lang.Math.sin(double)") \
+ V(MathCos, "double java.lang.Math.cos(double)") \
+ V(MathPow, "double java.lang.Math.pow(double, double)") \
V(ObjectHashCode, "int java.lang.Object.hashCode()") \
V(DoubleDoubleToRawLongBits, "long java.lang.Double.doubleToRawLongBits(double)") \
V(DexCacheGetDexNative, "com.android.dex.Dex java.lang.DexCache.getDexNative()") \
@@ -47,9 +54,9 @@
V(MemoryPeekInt, "int libcore.io.Memory.peekIntNative(long)") \
V(MemoryPeekLong, "long libcore.io.Memory.peekLongNative(long)") \
V(MemoryPeekByteArray, "void libcore.io.Memory.peekByteArray(long, byte[], int, int)") \
+ V(MethodInvoke, "java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[])") \
V(ReferenceGetReferent, "java.lang.Object java.lang.ref.Reference.getReferent()") \
V(RuntimeAvailableProcessors, "int java.lang.Runtime.availableProcessors()") \
- V(SecurityGetSecurityPropertiesReader, "java.io.Reader java.security.Security.getSecurityPropertiesReader()") \
V(StringGetCharsNoCheck, "void java.lang.String.getCharsNoCheck(int, int, char[], int)") \
V(StringCharAt, "char java.lang.String.charAt(int)") \
V(StringSetCharAt, "void java.lang.String.setCharAt(int, char)") \
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 1b5b665..b26635c 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -17,6 +17,7 @@
#include "unstarted_runtime.h"
#include <limits>
+#include <locale>
#include "base/casts.h"
#include "class_linker.h"
@@ -30,6 +31,7 @@
#include "runtime.h"
#include "scoped_thread_state_change.h"
#include "thread.h"
+#include "transaction.h"
namespace art {
namespace interpreter {
@@ -182,6 +184,16 @@
EXPECT_EQ(expect_int64t, result_int64t) << result.GetD() << " vs " << test_pairs[i][1];
}
}
+
+ // Prepare for aborts. Aborts assume that the exception class is already resolved, as the
+ // loading code doesn't work under transactions.
+ void PrepareForAborts() SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Object* result = Runtime::Current()->GetClassLinker()->FindClass(
+ Thread::Current(),
+ Transaction::kAbortExceptionSignature,
+ ScopedNullHandle<mirror::ClassLoader>());
+ CHECK(result != nullptr);
+ }
};
TEST_F(UnstartedRuntimeTest, MemoryPeekByte) {
@@ -689,5 +701,166 @@
ShadowFrame::DeleteDeoptimizedFrame(tmp);
}
+TEST_F(UnstartedRuntimeTest, ToLowerUpper) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+ std::locale c_locale("C");
+
+ // Check ASCII.
+ for (uint32_t i = 0; i < 128; ++i) {
+ bool c_upper = std::isupper(static_cast<char>(i), c_locale);
+ bool c_lower = std::islower(static_cast<char>(i), c_locale);
+ EXPECT_FALSE(c_upper && c_lower) << i;
+
+ // Check toLowerCase.
+ {
+ JValue result;
+ tmp->SetVReg(0, static_cast<int32_t>(i));
+ UnstartedCharacterToLowerCase(self, tmp, &result, 0);
+ ASSERT_FALSE(self->IsExceptionPending());
+ uint32_t lower_result = static_cast<uint32_t>(result.GetI());
+ if (c_lower) {
+ EXPECT_EQ(i, lower_result);
+ } else if (c_upper) {
+ EXPECT_EQ(static_cast<uint32_t>(std::tolower(static_cast<char>(i), c_locale)),
+ lower_result);
+ } else {
+ EXPECT_EQ(i, lower_result);
+ }
+ }
+
+ // Check toUpperCase.
+ {
+ JValue result2;
+ tmp->SetVReg(0, static_cast<int32_t>(i));
+ UnstartedCharacterToUpperCase(self, tmp, &result2, 0);
+ ASSERT_FALSE(self->IsExceptionPending());
+ uint32_t upper_result = static_cast<uint32_t>(result2.GetI());
+ if (c_upper) {
+ EXPECT_EQ(i, upper_result);
+ } else if (c_lower) {
+ EXPECT_EQ(static_cast<uint32_t>(std::toupper(static_cast<char>(i), c_locale)),
+ upper_result);
+ } else {
+ EXPECT_EQ(i, upper_result);
+ }
+ }
+ }
+
+ // Check abort for other things. Can't test all.
+
+ PrepareForAborts();
+
+ for (uint32_t i = 128; i < 256; ++i) {
+ {
+ JValue result;
+ tmp->SetVReg(0, static_cast<int32_t>(i));
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ UnstartedCharacterToLowerCase(self, tmp, &result, 0);
+ Runtime::Current()->ExitTransactionMode();
+ ASSERT_TRUE(self->IsExceptionPending());
+ ASSERT_TRUE(transaction.IsAborted());
+ }
+ {
+ JValue result;
+ tmp->SetVReg(0, static_cast<int32_t>(i));
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ UnstartedCharacterToUpperCase(self, tmp, &result, 0);
+ Runtime::Current()->ExitTransactionMode();
+ ASSERT_TRUE(self->IsExceptionPending());
+ ASSERT_TRUE(transaction.IsAborted());
+ }
+ }
+ for (uint64_t i = 256; i <= std::numeric_limits<uint32_t>::max(); i <<= 1) {
+ {
+ JValue result;
+ tmp->SetVReg(0, static_cast<int32_t>(i));
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ UnstartedCharacterToLowerCase(self, tmp, &result, 0);
+ Runtime::Current()->ExitTransactionMode();
+ ASSERT_TRUE(self->IsExceptionPending());
+ ASSERT_TRUE(transaction.IsAborted());
+ }
+ {
+ JValue result;
+ tmp->SetVReg(0, static_cast<int32_t>(i));
+ Transaction transaction;
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ UnstartedCharacterToUpperCase(self, tmp, &result, 0);
+ Runtime::Current()->ExitTransactionMode();
+ ASSERT_TRUE(self->IsExceptionPending());
+ ASSERT_TRUE(transaction.IsAborted());
+ }
+ }
+
+ ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+TEST_F(UnstartedRuntimeTest, Sin) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+ // Test an important value, PI/6. That's the one we see in practice.
+ constexpr uint64_t lvalue = UINT64_C(0x3fe0c152382d7365);
+ tmp->SetVRegLong(0, static_cast<int64_t>(lvalue));
+
+ JValue result;
+ UnstartedMathSin(self, tmp, &result, 0);
+
+ const uint64_t lresult = static_cast<uint64_t>(result.GetJ());
+ EXPECT_EQ(UINT64_C(0x3fdfffffffffffff), lresult);
+
+ ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+TEST_F(UnstartedRuntimeTest, Cos) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+ // Test an important value, PI/6. That's the one we see in practice.
+ constexpr uint64_t lvalue = UINT64_C(0x3fe0c152382d7365);
+ tmp->SetVRegLong(0, static_cast<int64_t>(lvalue));
+
+ JValue result;
+ UnstartedMathCos(self, tmp, &result, 0);
+
+ const uint64_t lresult = static_cast<uint64_t>(result.GetJ());
+ EXPECT_EQ(UINT64_C(0x3febb67ae8584cab), lresult);
+
+ ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+TEST_F(UnstartedRuntimeTest, Pow) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+ // Test an important pair.
+ constexpr uint64_t lvalue1 = UINT64_C(0x4079000000000000);
+ constexpr uint64_t lvalue2 = UINT64_C(0xbfe6db6dc0000000);
+
+ tmp->SetVRegLong(0, static_cast<int64_t>(lvalue1));
+ tmp->SetVRegLong(2, static_cast<int64_t>(lvalue2));
+
+ JValue result;
+ UnstartedMathPow(self, tmp, &result, 0);
+
+ const uint64_t lresult = static_cast<uint64_t>(result.GetJ());
+ EXPECT_EQ(UINT64_C(0x3f8c5c51326aa7ee), lresult);
+
+ ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
} // namespace interpreter
} // namespace art
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 558e443..2a66847 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -97,8 +97,9 @@
LOG(FATAL) << "Priority thread weight cannot be 0.";
}
} else {
- jit_options->priority_thread_weight_ =
- std::max(jit_options->compile_threshold_ / 2000, static_cast<size_t>(1));
+ jit_options->priority_thread_weight_ = std::max(
+ jit_options->warmup_threshold_ / Jit::kDefaultPriorityThreadWeightRatio,
+ static_cast<size_t>(1));
}
return jit_options;
@@ -154,6 +155,8 @@
jit->warm_method_threshold_ = options->GetWarmupThreshold();
jit->osr_method_threshold_ = options->GetOsrThreshold();
jit->priority_thread_weight_ = options->GetPriorityThreadWeight();
+ jit->transition_weight_ = std::max(
+ jit->warm_method_threshold_ / kDefaultTransitionRatio, static_cast<size_t>(1));
jit->CreateThreadPool();
@@ -240,8 +243,17 @@
if (!code_cache_->NotifyCompilationOf(method_to_compile, self, osr)) {
return false;
}
+
+ VLOG(jit) << "Compiling method "
+ << PrettyMethod(method_to_compile)
+ << " osr=" << std::boolalpha << osr;
bool success = jit_compile_method_(jit_compiler_handle_, method_to_compile, self, osr);
code_cache_->DoneCompiling(method_to_compile, self, osr);
+ if (!success) {
+ VLOG(jit) << "Failed to compile method "
+ << PrettyMethod(method_to_compile)
+ << " osr=" << std::boolalpha << osr;
+ }
return success;
}
@@ -520,15 +532,9 @@
void Run(Thread* self) OVERRIDE {
ScopedObjectAccess soa(self);
if (kind_ == kCompile) {
- VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_);
- if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false)) {
- VLOG(jit) << "Failed to compile method " << PrettyMethod(method_);
- }
+ Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false);
} else if (kind_ == kCompileOsr) {
- VLOG(jit) << "JitCompileTask compiling method osr " << PrettyMethod(method_);
- if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true)) {
- VLOG(jit) << "Failed to compile method osr " << PrettyMethod(method_);
- }
+ Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true);
} else {
DCHECK(kind_ == kAllocateProfile);
if (ProfilingInfo::Create(self, method_, /* retry_allocation */ true)) {
@@ -549,7 +555,7 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
};
-void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count) {
+void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_backedges) {
if (thread_pool_ == nullptr) {
// Should only see this when shutting down.
DCHECK(Runtime::Current()->IsShuttingDown(self));
@@ -573,7 +579,8 @@
}
int32_t new_count = starting_count + count; // int32 here to avoid wrap-around;
if (starting_count < warm_method_threshold_) {
- if (new_count >= warm_method_threshold_) {
+ if ((new_count >= warm_method_threshold_) &&
+ (method->GetProfilingInfo(sizeof(void*)) == nullptr)) {
bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false);
if (success) {
VLOG(jit) << "Start profiling " << PrettyMethod(method);
@@ -595,14 +602,19 @@
// Avoid jumping more than one state at a time.
new_count = std::min(new_count, hot_method_threshold_ - 1);
} else if (starting_count < hot_method_threshold_) {
- if (new_count >= hot_method_threshold_) {
+ if ((new_count >= hot_method_threshold_) &&
+ !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
DCHECK(thread_pool_ != nullptr);
thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
}
// Avoid jumping more than one state at a time.
new_count = std::min(new_count, osr_method_threshold_ - 1);
} else if (starting_count < osr_method_threshold_) {
- if (new_count >= osr_method_threshold_) {
+ if (!with_backedges) {
+ // If the samples don't contain any back edge, we don't increment the hotness.
+ return;
+ }
+ if ((new_count >= osr_method_threshold_) && !code_cache_->IsOsrCompiled(method)) {
DCHECK(thread_pool_ != nullptr);
thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
}
@@ -623,14 +635,11 @@
ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*));
// Update the entrypoint if the ProfilingInfo has one. The interpreter will call it
// instead of interpreting the method.
- // We avoid doing this if exit stubs are installed to not mess with the instrumentation.
- // TODO(ngeoffray): Clean up instrumentation and code cache interactions.
- if ((profiling_info != nullptr) &&
- (profiling_info->GetSavedEntryPoint() != nullptr) &&
- !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
- method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint());
+ if ((profiling_info != nullptr) && (profiling_info->GetSavedEntryPoint() != nullptr)) {
+ Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(
+ method, profiling_info->GetSavedEntryPoint());
} else {
- AddSamples(thread, method, 1);
+ AddSamples(thread, method, 1, /* with_backedges */false);
}
}
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 96f9608..ff3acf6 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -43,6 +43,8 @@
public:
static constexpr bool kStressMode = kIsDebugBuild;
static constexpr size_t kDefaultCompileThreshold = kStressMode ? 2 : 10000;
+ static constexpr size_t kDefaultPriorityThreadWeightRatio = 1000;
+ static constexpr size_t kDefaultTransitionRatio = 100;
virtual ~Jit();
static Jit* Create(JitOptions* options, std::string* error_msg);
@@ -92,7 +94,7 @@
void MethodEntered(Thread* thread, ArtMethod* method)
SHARED_REQUIRES(Locks::mutator_lock_);
- void AddSamples(Thread* self, ArtMethod* method, uint16_t samples)
+ void AddSamples(Thread* self, ArtMethod* method, uint16_t samples, bool with_backedges)
SHARED_REQUIRES(Locks::mutator_lock_);
void InvokeVirtualOrInterface(Thread* thread,
@@ -102,6 +104,16 @@
ArtMethod* callee)
SHARED_REQUIRES(Locks::mutator_lock_);
+ void NotifyInterpreterToCompiledCodeTransition(Thread* self, ArtMethod* caller)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ AddSamples(self, caller, transition_weight_, false);
+ }
+
+ void NotifyCompiledCodeToInterpreterTransition(Thread* self, ArtMethod* callee)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ AddSamples(self, callee, transition_weight_, false);
+ }
+
// Starts the profile saver if the config options allow profile recording.
// The profile will be stored in the specified `filename` and will contain
// information collected from the given `code_paths` (a set of dex locations).
@@ -175,6 +187,7 @@
uint16_t warm_method_threshold_;
uint16_t osr_method_threshold_;
uint16_t priority_thread_weight_;
+ uint16_t transition_weight_;
std::unique_ptr<ThreadPool> thread_pool_;
DISALLOW_COPY_AND_ASSIGN(Jit);
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 820ae6a..752d4ba 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -354,8 +354,7 @@
if (osr) {
number_of_osr_compilations_++;
osr_code_map_.Put(method, code_ptr);
- } else if (!Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
- // TODO(ngeoffray): Clean up instrumentation and code cache interactions.
+ } else {
Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(
method, method_header->GetEntryPoint());
}
@@ -366,7 +365,7 @@
}
last_update_time_ns_.StoreRelease(NanoTime());
VLOG(jit)
- << "JIT added (osr = " << std::boolalpha << osr << std::noboolalpha << ") "
+ << "JIT added (osr=" << std::boolalpha << osr << std::noboolalpha << ") "
<< PrettyMethod(method) << "@" << method
<< " ccache_size=" << PrettySize(CodeCacheSizeLocked()) << ": "
<< " dcache_size=" << PrettySize(DataCacheSizeLocked()) << ": "
@@ -634,10 +633,7 @@
bool next_collection_will_be_full = ShouldDoFullCollection();
// Start polling the liveness of compiled code to prepare for the next full collection.
- // We avoid doing this if exit stubs are installed to not mess with the instrumentation.
- // TODO(ngeoffray): Clean up instrumentation and code cache interactions.
- if (!Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled() &&
- next_collection_will_be_full) {
+ if (next_collection_will_be_full) {
// Save the entry point of methods we have compiled, and update the entry
// point of those methods to the interpreter. If the method is invoked, the
// interpreter will update its entry point to the compiled code and call it.
@@ -645,7 +641,8 @@
const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode();
if (ContainsPc(entry_point)) {
info->SetSavedEntryPoint(entry_point);
- info->GetMethod()->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
+ Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(
+ info->GetMethod(), GetQuickToInterpreterBridge());
}
}
@@ -905,15 +902,18 @@
return last_update_time_ns_.LoadAcquire();
}
+bool JitCodeCache::IsOsrCompiled(ArtMethod* method) {
+ MutexLock mu(Thread::Current(), lock_);
+ return osr_code_map_.find(method) != osr_code_map_.end();
+}
+
bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr) {
if (!osr && ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
- VLOG(jit) << PrettyMethod(method) << " is already compiled";
return false;
}
MutexLock mu(self, lock_);
if (osr && (osr_code_map_.find(method) != osr_code_map_.end())) {
- VLOG(jit) << PrettyMethod(method) << " is already osr compiled";
return false;
}
@@ -928,7 +928,6 @@
}
if (info->IsMethodBeingCompiled(osr)) {
- VLOG(jit) << PrettyMethod(method) << " is already being compiled";
return false;
}
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 9f18c70..f31cc51 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -186,6 +186,8 @@
void Dump(std::ostream& os) REQUIRES(!lock_);
+ bool IsOsrCompiled(ArtMethod* method) REQUIRES(!lock_);
+
private:
// Take ownership of maps.
JitCodeCache(MemMap* code_map,
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index c718466..04ba8df 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -2286,16 +2286,16 @@
// Test the offset computation of JNIEnvExt offsets. b/26071368.
TEST_F(JniInternalTest, JNIEnvExtOffsets) {
EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie),
- JNIEnvExt::LocalRefCookieOffset(sizeof(void*)).Int32Value());
+ JNIEnvExt::LocalRefCookieOffset(sizeof(void*)).Uint32Value());
- EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, self), JNIEnvExt::SelfOffset(sizeof(void*)).Int32Value());
+ EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, self), JNIEnvExt::SelfOffset(sizeof(void*)).Uint32Value());
// segment_state_ is private in the IndirectReferenceTable. So this test isn't as good as we'd
// hope it to be.
- int32_t segment_state_now =
+ uint32_t segment_state_now =
OFFSETOF_MEMBER(JNIEnvExt, locals) +
- IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Int32Value();
- int32_t segment_state_computed = JNIEnvExt::SegmentStateOffset(sizeof(void*)).Int32Value();
+ IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Uint32Value();
+ uint32_t segment_state_computed = JNIEnvExt::SegmentStateOffset(sizeof(void*)).Uint32Value();
EXPECT_EQ(segment_state_now, segment_state_computed);
}
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 42f003d..b4a23ba 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -1165,5 +1165,16 @@
mirror::Class* klass,
mirror::ObjectArray<mirror::Class>* args);
+int32_t Class::GetInnerClassFlags(Handle<Class> h_this, int32_t default_value) {
+ if (h_this->IsProxyClass() || h_this->GetDexCache() == nullptr) {
+ return default_value;
+ }
+ uint32_t flags;
+ if (!h_this->GetDexFile().GetInnerClassFlags(h_this, &flags)) {
+ return default_value;
+ }
+ return flags;
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 57c3590..5b6ded1 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1223,6 +1223,9 @@
Thread* self, Handle<mirror::ObjectArray<mirror::Class>> args, size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
+ static int32_t GetInnerClassFlags(Handle<Class> h_this, int32_t default_value)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
// Used to initialize a class in the allocation code path to ensure it is guarded by a StoreStore
// fence.
class InitializeClassVisitor {
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 0e7f7f3..6285542 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -33,7 +33,7 @@
namespace mirror {
inline uint32_t String::ClassSize(size_t pointer_size) {
- uint32_t vtable_entries = Object::kVTableLength + 52;
+ uint32_t vtable_entries = Object::kVTableLength + 56;
return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 2, pointer_size);
}
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 6290cb2..3680c78 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -215,6 +215,26 @@
obj_ = GcRoot<mirror::Object>(object);
}
+std::string Monitor::PrettyContentionInfo(const std::string& owner_name,
+ pid_t owner_tid,
+ ArtMethod* owners_method,
+ uint32_t owners_dex_pc,
+ size_t num_waiters) {
+ const char* owners_filename;
+ int32_t owners_line_number = 0;
+ if (owners_method != nullptr) {
+ TranslateLocation(owners_method, owners_dex_pc, &owners_filename, &owners_line_number);
+ }
+ std::ostringstream oss;
+ oss << "monitor contention with owner " << owner_name << " (" << owner_tid << ")";
+ if (owners_method != nullptr) {
+ oss << " owner method=" << PrettyMethod(owners_method);
+ oss << " from " << owners_filename << ":" << owners_line_number;
+ }
+ oss << " waiters=" << num_waiters;
+ return oss.str();
+}
+
void Monitor::Lock(Thread* self) {
MutexLock mu(self, monitor_lock_);
while (true) {
@@ -242,36 +262,83 @@
monitor_lock_.Unlock(self); // Let go of locks in order.
self->SetMonitorEnterObject(GetObject());
{
+ uint32_t original_owner_thread_id = 0u;
ScopedThreadStateChange tsc(self, kBlocked); // Change to blocked and give up mutator_lock_.
- // Reacquire monitor_lock_ without mutator_lock_ for Wait.
- MutexLock mu2(self, monitor_lock_);
- if (owner_ != nullptr) { // Did the owner_ give the lock up?
- if (ATRACE_ENABLED()) {
- std::string name;
- owner_->GetThreadName(name);
- ATRACE_BEGIN(("Contended on monitor with owner " + name).c_str());
+ {
+ // Reacquire monitor_lock_ without mutator_lock_ for Wait.
+ MutexLock mu2(self, monitor_lock_);
+ if (owner_ != nullptr) { // Did the owner_ give the lock up?
+ original_owner_thread_id = owner_->GetThreadId();
+ if (ATRACE_ENABLED()) {
+ std::ostringstream oss;
+ std::string name;
+ owner_->GetThreadName(name);
+ oss << PrettyContentionInfo(name,
+ owner_->GetTid(),
+ owners_method,
+ owners_dex_pc,
+ num_waiters);
+ // Add info for contending thread.
+ uint32_t pc;
+ ArtMethod* m = self->GetCurrentMethod(&pc);
+ const char* filename;
+ int32_t line_number;
+ TranslateLocation(m, pc, &filename, &line_number);
+ oss << " blocking from " << (filename != nullptr ? filename : "null")
+ << ":" << line_number;
+ ATRACE_BEGIN(oss.str().c_str());
+ }
+ monitor_contenders_.Wait(self); // Still contended so wait.
}
- monitor_contenders_.Wait(self); // Still contended so wait.
+ }
+ if (original_owner_thread_id != 0u) {
// Woken from contention.
if (log_contention) {
- uint64_t wait_ms = MilliTime() - wait_start_ms;
- uint32_t sample_percent;
- if (wait_ms >= lock_profiling_threshold_) {
- sample_percent = 100;
- } else {
- sample_percent = 100 * wait_ms / lock_profiling_threshold_;
- }
- if (sample_percent != 0 && (static_cast<uint32_t>(rand() % 100) < sample_percent)) {
- const char* owners_filename;
- int32_t owners_line_number;
- TranslateLocation(owners_method, owners_dex_pc, &owners_filename, &owners_line_number);
- if (wait_ms > kLongWaitMs && owners_method != nullptr) {
- LOG(WARNING) << "Long monitor contention event with owner method="
- << PrettyMethod(owners_method) << " from " << owners_filename << ":"
- << owners_line_number << " waiters=" << num_waiters << " for "
- << PrettyDuration(MsToNs(wait_ms));
+ uint32_t original_owner_tid = 0;
+ std::string original_owner_name;
+ {
+ MutexLock mu2(Thread::Current(), *Locks::thread_list_lock_);
+ // Re-find the owner in case the thread got killed.
+ Thread* original_owner = Runtime::Current()->GetThreadList()->FindThreadByThreadId(
+ original_owner_thread_id);
+ // Do not do any work that requires the mutator lock.
+ if (original_owner != nullptr) {
+ original_owner_tid = original_owner->GetTid();
+ original_owner->GetThreadName(original_owner_name);
}
- LogContentionEvent(self, wait_ms, sample_percent, owners_filename, owners_line_number);
+ }
+
+ if (original_owner_tid != 0u) {
+ uint64_t wait_ms = MilliTime() - wait_start_ms;
+ uint32_t sample_percent;
+ if (wait_ms >= lock_profiling_threshold_) {
+ sample_percent = 100;
+ } else {
+ sample_percent = 100 * wait_ms / lock_profiling_threshold_;
+ }
+ if (sample_percent != 0 && (static_cast<uint32_t>(rand() % 100) < sample_percent)) {
+ if (wait_ms > kLongWaitMs && owners_method != nullptr) {
+ // TODO: We should maybe check that original_owner is still a live thread.
+ LOG(WARNING) << "Long "
+ << PrettyContentionInfo(original_owner_name,
+ original_owner_tid,
+ owners_method,
+ owners_dex_pc,
+ num_waiters)
+ << " for " << PrettyDuration(MsToNs(wait_ms));
+ }
+ const char* owners_filename;
+ int32_t owners_line_number;
+ TranslateLocation(owners_method,
+ owners_dex_pc,
+ &owners_filename,
+ &owners_line_number);
+ LogContentionEvent(self,
+ wait_ms,
+ sample_percent,
+ owners_filename,
+ owners_line_number);
+ }
}
}
ATRACE_END();
@@ -311,25 +378,34 @@
return oss.str();
}
-void Monitor::FailedUnlock(mirror::Object* o, Thread* expected_owner, Thread* found_owner,
+void Monitor::FailedUnlock(mirror::Object* o,
+ uint32_t expected_owner_thread_id,
+ uint32_t found_owner_thread_id,
Monitor* monitor) {
- Thread* current_owner = nullptr;
+ // Acquire thread list lock so threads won't disappear from under us.
std::string current_owner_string;
std::string expected_owner_string;
std::string found_owner_string;
+ uint32_t current_owner_thread_id = 0u;
{
- // TODO: isn't this too late to prevent threads from disappearing?
- // Acquire thread list lock so threads won't disappear from under us.
MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ ThreadList* const thread_list = Runtime::Current()->GetThreadList();
+ Thread* expected_owner = thread_list->FindThreadByThreadId(expected_owner_thread_id);
+ Thread* found_owner = thread_list->FindThreadByThreadId(found_owner_thread_id);
+
// Re-read owner now that we hold lock.
- current_owner = (monitor != nullptr) ? monitor->GetOwner() : nullptr;
+ Thread* current_owner = (monitor != nullptr) ? monitor->GetOwner() : nullptr;
+ if (current_owner != nullptr) {
+ current_owner_thread_id = current_owner->GetThreadId();
+ }
// Get short descriptions of the threads involved.
current_owner_string = ThreadToString(current_owner);
- expected_owner_string = ThreadToString(expected_owner);
- found_owner_string = ThreadToString(found_owner);
+ expected_owner_string = expected_owner != nullptr ? ThreadToString(expected_owner) : "unnamed";
+ found_owner_string = found_owner != nullptr ? ThreadToString(found_owner) : "unnamed";
}
- if (current_owner == nullptr) {
- if (found_owner == nullptr) {
+
+ if (current_owner_thread_id == 0u) {
+ if (found_owner_thread_id == 0u) {
ThrowIllegalMonitorStateExceptionF("unlock of unowned monitor on object of type '%s'"
" on thread '%s'",
PrettyTypeOf(o).c_str(),
@@ -343,7 +419,7 @@
expected_owner_string.c_str());
}
} else {
- if (found_owner == nullptr) {
+ if (found_owner_thread_id == 0u) {
// Race: originally there was no owner, there is now
ThrowIllegalMonitorStateExceptionF("unlock of monitor owned by '%s' on object of type '%s'"
" (originally believed to be unowned) on thread '%s'",
@@ -351,7 +427,7 @@
PrettyTypeOf(o).c_str(),
expected_owner_string.c_str());
} else {
- if (found_owner != current_owner) {
+ if (found_owner_thread_id != current_owner_thread_id) {
// Race: originally found and current owner have changed
ThrowIllegalMonitorStateExceptionF("unlock of monitor originally owned by '%s' (now"
" owned by '%s') on object of type '%s' on thread '%s'",
@@ -372,27 +448,31 @@
bool Monitor::Unlock(Thread* self) {
DCHECK(self != nullptr);
- MutexLock mu(self, monitor_lock_);
- Thread* owner = owner_;
- if (owner == self) {
- // We own the monitor, so nobody else can be in here.
- if (lock_count_ == 0) {
- owner_ = nullptr;
- locking_method_ = nullptr;
- locking_dex_pc_ = 0;
- // Wake a contender.
- monitor_contenders_.Signal(self);
- } else {
- --lock_count_;
+ uint32_t owner_thread_id = 0u;
+ {
+ MutexLock mu(self, monitor_lock_);
+ Thread* owner = owner_;
+ if (owner != nullptr) {
+ owner_thread_id = owner->GetThreadId();
}
- } else {
- // We don't own this, so we're not allowed to unlock it.
- // The JNI spec says that we should throw IllegalMonitorStateException
- // in this case.
- FailedUnlock(GetObject(), self, owner, this);
- return false;
+ if (owner == self) {
+ // We own the monitor, so nobody else can be in here.
+ if (lock_count_ == 0) {
+ owner_ = nullptr;
+ locking_method_ = nullptr;
+ locking_dex_pc_ = 0;
+ // Wake a contender.
+ monitor_contenders_.Signal(self);
+ } else {
+ --lock_count_;
+ }
+ return true;
+ }
}
- return true;
+ // We don't own this, so we're not allowed to unlock it.
+ // The JNI spec says that we should throw IllegalMonitorStateException in this case.
+ FailedUnlock(GetObject(), self->GetThreadId(), owner_thread_id, this);
+ return false;
}
void Monitor::Wait(Thread* self, int64_t ms, int32_t ns,
@@ -769,16 +849,13 @@
case LockWord::kHashCode:
// Fall-through.
case LockWord::kUnlocked:
- FailedUnlock(h_obj.Get(), self, nullptr, nullptr);
+ FailedUnlock(h_obj.Get(), self->GetThreadId(), 0u, nullptr);
return false; // Failure.
case LockWord::kThinLocked: {
uint32_t thread_id = self->GetThreadId();
uint32_t owner_thread_id = lock_word.ThinLockOwner();
if (owner_thread_id != thread_id) {
- // TODO: there's a race here with the owner dying while we unlock.
- Thread* owner =
- Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner());
- FailedUnlock(h_obj.Get(), self, owner, nullptr);
+ FailedUnlock(h_obj.Get(), thread_id, owner_thread_id, nullptr);
return false; // Failure.
} else {
// We own the lock, decrease the recursion count.
@@ -1072,8 +1149,10 @@
return owner_ != nullptr;
}
-void Monitor::TranslateLocation(ArtMethod* method, uint32_t dex_pc,
- const char** source_file, int32_t* line_number) const {
+void Monitor::TranslateLocation(ArtMethod* method,
+ uint32_t dex_pc,
+ const char** source_file,
+ int32_t* line_number) {
// If method is null, location is unknown
if (method == nullptr) {
*source_file = "";
diff --git a/runtime/monitor.h b/runtime/monitor.h
index ae9b3cc..8c7496b 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -185,9 +185,12 @@
const char* owner_filename, int32_t owner_line_number)
SHARED_REQUIRES(Locks::mutator_lock_);
- static void FailedUnlock(mirror::Object* obj, Thread* expected_owner, Thread* found_owner,
+ static void FailedUnlock(mirror::Object* obj,
+ uint32_t expected_owner_thread_id,
+ uint32_t found_owner_thread_id,
Monitor* mon)
- REQUIRES(!Locks::thread_list_lock_)
+ REQUIRES(!Locks::thread_list_lock_,
+ !monitor_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
void Lock(Thread* self)
@@ -208,6 +211,13 @@
REQUIRES(!monitor_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
+ static std::string PrettyContentionInfo(const std::string& owner_name,
+ pid_t owner_tid,
+ ArtMethod* owners_method,
+ uint32_t owners_dex_pc,
+ size_t num_waiters)
+ REQUIRES(!Locks::thread_list_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Wait on a monitor until timeout, interrupt, or notification. Used for Object.wait() and
// (somewhat indirectly) Thread.sleep() and Thread.join().
@@ -233,8 +243,9 @@
SHARED_REQUIRES(Locks::mutator_lock_);
// Translates the provided method and pc into its declaring class' source file and line number.
- void TranslateLocation(ArtMethod* method, uint32_t pc,
- const char** source_file, int32_t* line_number) const
+ static void TranslateLocation(ArtMethod* method, uint32_t pc,
+ const char** source_file,
+ int32_t* line_number)
SHARED_REQUIRES(Locks::mutator_lock_);
uint32_t GetOwnerThreadId() REQUIRES(!monitor_lock_);
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index c1899af..6b7ca40 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -517,14 +517,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
- if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
- return defaultValue;
- }
- uint32_t flags;
- if (!klass->GetDexFile().GetInnerClassFlags(klass, &flags)) {
- return defaultValue;
- }
- return flags;
+ return mirror::Class::GetInnerClassFlags(klass, defaultValue);
}
static jstring Class_getInnerClassName(JNIEnv* env, jobject javaThis) {
diff --git a/runtime/thread.h b/runtime/thread.h
index 2218b5a..ed42e46 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -312,6 +312,7 @@
*/
static int GetNativePriority();
+ // Guaranteed to be non-zero.
uint32_t GetThreadId() const {
return tls32_.thin_lock_thread_id;
}
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index a9ce056..1e5264c 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -923,12 +923,9 @@
}
}
-Thread* ThreadList::FindThreadByThreadId(uint32_t thin_lock_id) {
- Thread* self = Thread::Current();
- MutexLock mu(self, *Locks::thread_list_lock_);
+Thread* ThreadList::FindThreadByThreadId(uint32_t thread_id) {
for (const auto& thread : list_) {
- if (thread->GetThreadId() == thin_lock_id) {
- CHECK(thread == self || thread->IsSuspended());
+ if (thread->GetThreadId() == thread_id) {
return thread;
}
}
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index f97ecd3..df81ad1 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -89,8 +89,8 @@
!Locks::thread_list_lock_,
!Locks::thread_suspend_count_lock_);
- // Find an already suspended thread (or self) by its id.
- Thread* FindThreadByThreadId(uint32_t thin_lock_id);
+ // Find an existing thread (or self) by its thread id (not tid).
+ Thread* FindThreadByThreadId(uint32_t thread_id) REQUIRES(Locks::thread_list_lock_);
// Run a checkpoint on threads, running threads are not suspended but run the checkpoint inside
// of the suspend check. Returns how many checkpoints that are expected to run, including for
diff --git a/test/567-checker-compare/src/Main.java b/test/567-checker-compare/src/Main.java
index f95ff1a..8587950 100644
--- a/test/567-checker-compare/src/Main.java
+++ b/test/567-checker-compare/src/Main.java
@@ -16,6 +16,32 @@
public class Main {
+ public static boolean doThrow = false;
+
+ /// CHECK-START: void Main.$opt$noinline$testReplaceInputWithItself(int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<ArgX:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Cmp:i\d+>> InvokeStaticOrDirect [<<ArgX>>,<<Zero>>,<<Method>>] intrinsic:IntegerCompare
+ /// CHECK-DAG: GreaterThanOrEqual [<<Cmp>>,<<Zero>>]
+
+ /// CHECK-START: void Main.$opt$noinline$testReplaceInputWithItself(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<ArgX:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: GreaterThanOrEqual [<<ArgX>>,<<Zero>>]
+
+ public static void $opt$noinline$testReplaceInputWithItself(int x) {
+ if (doThrow) { throw new Error(); }
+
+ // The instruction simplifier first replaces Integer.compare(x, 0) with Compare HIR
+ // and then merges the Compare into the GreaterThanOrEqual. This is a regression
+ // test that to check that it is allowed to replace the second input of the
+ // GreaterThanOrEqual, i.e. <<Zero>>, with the very same instruction.
+ if (Integer.compare(x, 0) < 0) {
+ System.out.println("OOOPS");
+ }
+ }
+
/// CHECK-START: int Main.compareBooleans(boolean, boolean) intrinsics_recognition (after)
/// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
/// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
@@ -890,6 +916,8 @@
public static void main(String args[]) {
+ $opt$noinline$testReplaceInputWithItself(42);
+
testCompareBooleans();
testCompareBytes();
testCompareShorts();
diff --git a/test/570-checker-osr/expected.txt b/test/570-checker-osr/expected.txt
index 25fb220..65447be 100644
--- a/test/570-checker-osr/expected.txt
+++ b/test/570-checker-osr/expected.txt
@@ -3,3 +3,4 @@
200000
300000
400000
+b28210356 passed.
diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc
index 09e97ea..2a5b2c9 100644
--- a/test/570-checker-osr/osr.cc
+++ b/test/570-checker-osr/osr.cc
@@ -20,15 +20,17 @@
#include "jit/profiling_info.h"
#include "oat_quick_method_header.h"
#include "scoped_thread_state_change.h"
+#include "ScopedUtfChars.h"
#include "stack_map.h"
namespace art {
class OsrVisitor : public StackVisitor {
public:
- explicit OsrVisitor(Thread* thread)
+ explicit OsrVisitor(Thread* thread, const char* method_name)
SHARED_REQUIRES(Locks::mutator_lock_)
: StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ method_name_(method_name),
in_osr_method_(false),
in_interpreter_(false) {}
@@ -36,13 +38,7 @@
ArtMethod* m = GetMethod();
std::string m_name(m->GetName());
- if ((m_name.compare("$noinline$returnInt") == 0) ||
- (m_name.compare("$noinline$returnFloat") == 0) ||
- (m_name.compare("$noinline$returnDouble") == 0) ||
- (m_name.compare("$noinline$returnLong") == 0) ||
- (m_name.compare("$noinline$deopt") == 0) ||
- (m_name.compare("$noinline$inlineCache") == 0) ||
- (m_name.compare("$noinline$stackOverflow") == 0)) {
+ if (m_name.compare(method_name_) == 0) {
const OatQuickMethodHeader* header =
Runtime::Current()->GetJit()->GetCodeCache()->LookupOsrMethodHeader(m);
if (header != nullptr && header == GetCurrentOatQuickMethodHeader()) {
@@ -55,74 +51,89 @@
return true;
}
+ const char* const method_name_;
bool in_osr_method_;
bool in_interpreter_;
};
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_ensureInOsrCode(JNIEnv*, jclass) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInOsrCode(JNIEnv* env,
+ jclass,
+ jstring method_name) {
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit == nullptr) {
// Just return true for non-jit configurations to stop the infinite loop.
return JNI_TRUE;
}
+ ScopedUtfChars chars(env, method_name);
+ CHECK(chars.c_str() != nullptr);
ScopedObjectAccess soa(Thread::Current());
- OsrVisitor visitor(soa.Self());
+ OsrVisitor visitor(soa.Self(), chars.c_str());
visitor.WalkStack();
return visitor.in_osr_method_;
}
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_ensureInInterpreter(JNIEnv*, jclass) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInInterpreter(JNIEnv* env,
+ jclass,
+ jstring method_name) {
if (!Runtime::Current()->UseJit()) {
// The return value is irrelevant if we're not using JIT.
return false;
}
+ ScopedUtfChars chars(env, method_name);
+ CHECK(chars.c_str() != nullptr);
ScopedObjectAccess soa(Thread::Current());
- OsrVisitor visitor(soa.Self());
+ OsrVisitor visitor(soa.Self(), chars.c_str());
visitor.WalkStack();
return visitor.in_interpreter_;
}
class ProfilingInfoVisitor : public StackVisitor {
public:
- explicit ProfilingInfoVisitor(Thread* thread)
+ explicit ProfilingInfoVisitor(Thread* thread, const char* method_name)
SHARED_REQUIRES(Locks::mutator_lock_)
- : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {}
+ : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ method_name_(method_name) {}
bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) {
ArtMethod* m = GetMethod();
std::string m_name(m->GetName());
- if ((m_name.compare("$noinline$inlineCache") == 0) ||
- (m_name.compare("$noinline$stackOverflow") == 0)) {
+ if (m_name.compare(method_name_) == 0) {
ProfilingInfo::Create(Thread::Current(), m, /* retry_allocation */ true);
return false;
}
return true;
}
+
+ const char* const method_name_;
};
-extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasProfilingInfo(JNIEnv*, jclass) {
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasProfilingInfo(JNIEnv* env,
+ jclass,
+ jstring method_name) {
if (!Runtime::Current()->UseJit()) {
return;
}
+ ScopedUtfChars chars(env, method_name);
+ CHECK(chars.c_str() != nullptr);
ScopedObjectAccess soa(Thread::Current());
- ProfilingInfoVisitor visitor(soa.Self());
+ ProfilingInfoVisitor visitor(soa.Self(), chars.c_str());
visitor.WalkStack();
}
class OsrCheckVisitor : public StackVisitor {
public:
- explicit OsrCheckVisitor(Thread* thread)
+ OsrCheckVisitor(Thread* thread, const char* method_name)
SHARED_REQUIRES(Locks::mutator_lock_)
- : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {}
+ : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ method_name_(method_name) {}
bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) {
ArtMethod* m = GetMethod();
std::string m_name(m->GetName());
jit::Jit* jit = Runtime::Current()->GetJit();
- if ((m_name.compare("$noinline$inlineCache") == 0) ||
- (m_name.compare("$noinline$stackOverflow") == 0)) {
+ if (m_name.compare(method_name_) == 0) {
while (jit->GetCodeCache()->LookupOsrMethodHeader(m) == nullptr) {
// Sleep to yield to the compiler thread.
sleep(0);
@@ -133,14 +144,20 @@
}
return true;
}
+
+ const char* const method_name_;
};
-extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasOsrCode(JNIEnv*, jclass) {
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasOsrCode(JNIEnv* env,
+ jclass,
+ jstring method_name) {
if (!Runtime::Current()->UseJit()) {
return;
}
+ ScopedUtfChars chars(env, method_name);
+ CHECK(chars.c_str() != nullptr);
ScopedObjectAccess soa(Thread::Current());
- OsrCheckVisitor visitor(soa.Self());
+ OsrCheckVisitor visitor(soa.Self(), chars.c_str());
visitor.WalkStack();
}
diff --git a/test/570-checker-osr/src/Main.java b/test/570-checker-osr/src/Main.java
index 6514334..200b54a 100644
--- a/test/570-checker-osr/src/Main.java
+++ b/test/570-checker-osr/src/Main.java
@@ -63,6 +63,9 @@
$noinline$stackOverflow(new Main(), /* isSecondInvocation */ false);
$noinline$stackOverflow(new SubMain(), /* isSecondInvocation */ true);
+
+ $opt$noinline$testOsrInlineLoop(null);
+ System.out.println("b28210356 passed.");
}
public static int $noinline$returnInt() {
@@ -70,7 +73,7 @@
int i = 0;
for (; i < 100000; ++i) {
}
- while (!ensureInOsrCode()) {}
+ while (!isInOsrCode("$noinline$returnInt")) {}
System.out.println(i);
return 53;
}
@@ -80,7 +83,7 @@
int i = 0;
for (; i < 200000; ++i) {
}
- while (!ensureInOsrCode()) {}
+ while (!isInOsrCode("$noinline$returnFloat")) {}
System.out.println(i);
return 42.2f;
}
@@ -90,7 +93,7 @@
int i = 0;
for (; i < 300000; ++i) {
}
- while (!ensureInOsrCode()) {}
+ while (!isInOsrCode("$noinline$returnDouble")) {}
System.out.println(i);
return Double.longBitsToDouble(0xF000000000001111L);
}
@@ -100,7 +103,7 @@
int i = 0;
for (; i < 400000; ++i) {
}
- while (!ensureInOsrCode()) {}
+ while (!isInOsrCode("$noinline$returnLong")) {}
System.out.println(i);
return 0xFFFF000000001111L;
}
@@ -110,22 +113,22 @@
int i = 0;
for (; i < 100000; ++i) {
}
- while (!ensureInOsrCode()) {}
+ while (!isInOsrCode("$noinline$deopt")) {}
DeoptimizationController.startDeoptimization();
}
public static Class $noinline$inlineCache(Main m, boolean isSecondInvocation) {
// If we are running in non-JIT mode, or were unlucky enough to get this method
// already JITted, just return the expected value.
- if (!ensureInInterpreter()) {
+ if (!isInInterpreter("$noinline$inlineCache")) {
return SubMain.class;
}
- ensureHasProfilingInfo();
+ ensureHasProfilingInfo("$noinline$inlineCache");
// Ensure that we have OSR code to jump to.
if (isSecondInvocation) {
- ensureHasOsrCode();
+ ensureHasOsrCode("$noinline$inlineCache");
}
// This call will be optimized in the OSR compiled code
@@ -137,7 +140,7 @@
// code we are jumping to will have wrongly optimize other as being a
// 'Main'.
if (isSecondInvocation) {
- while (!ensureInOsrCode()) {}
+ while (!isInOsrCode("$noinline$inlineCache")) {}
}
// We used to wrongly optimize this call and assume 'other' was a 'Main'.
@@ -159,16 +162,16 @@
public static void $noinline$stackOverflow(Main m, boolean isSecondInvocation) {
// If we are running in non-JIT mode, or were unlucky enough to get this method
// already JITted, just return the expected value.
- if (!ensureInInterpreter()) {
+ if (!isInInterpreter("$noinline$stackOverflow")) {
return;
}
// We need a ProfilingInfo object to populate the 'otherInlineCache' call.
- ensureHasProfilingInfo();
+ ensureHasProfilingInfo("$noinline$stackOverflow");
if (isSecondInvocation) {
// Ensure we have an OSR code and we jump to it.
- while (!ensureInOsrCode()) {}
+ while (!isInOsrCode("$noinline$stackOverflow")) {}
}
for (int i = 0; i < (isSecondInvocation ? 10000000 : 1); ++i) {
@@ -179,10 +182,46 @@
}
}
- public static native boolean ensureInInterpreter();
- public static native boolean ensureInOsrCode();
- public static native void ensureHasProfilingInfo();
- public static native void ensureHasOsrCode();
+ public static void $opt$noinline$testOsrInlineLoop(String[] args) {
+ // Regression test for inlining a method with a loop to a method without a loop in OSR mode.
+ if (doThrow) throw new Error();
+ assertIntEquals(12, $opt$inline$testRemoveSuspendCheck(12, 5));
+ // Since we cannot have a loop directly in this method, we need to force the OSR
+ // compilation from native code.
+ ensureHasProfilingInfo("$opt$noinline$testOsrInlineLoop");
+ ensureHasOsrCode("$opt$noinline$testOsrInlineLoop");
+ }
+
+ public static int $opt$inline$testRemoveSuspendCheck(int x, int y) {
+ // For this test we need an inlined loop and have DCE re-run loop analysis
+ // after inlining.
+ while (y > 0) {
+ while ($opt$inline$inlineFalse() || !$opt$inline$inlineTrue()) {
+ x++;
+ }
+ y--;
+ }
+ return x;
+ }
+
+ public static boolean $opt$inline$inlineTrue() {
+ return true;
+ }
+
+ public static boolean $opt$inline$inlineFalse() {
+ return false;
+ }
+
+ public static void assertIntEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static native boolean isInOsrCode(String methodName);
+ public static native boolean isInInterpreter(String methodName);
+ public static native void ensureHasProfilingInfo(String methodName);
+ public static native void ensureHasOsrCode(String methodName);
public static boolean doThrow = false;
}
diff --git a/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali b/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali
index 8e01084..366c7b9 100644
--- a/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali
+++ b/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali
@@ -40,6 +40,11 @@
invoke-static {v0}, Ljava/lang/System;->exit(I)V
goto :body1
+ # Trivially dead code to ensure linear order verification skips removed blocks (b/28252537).
+ :dead_code
+ nop
+ goto :dead_code
+
:header
mul-int/2addr p3, p3
if-eqz p1, :body2
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index 354fcef..b6a19b7 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -28,18 +28,6 @@
exit 1
fi
-if [ "x$ART_USE_READ_BARRIER" = xtrue ]; then
- # For the moment, skip JDWP tests when read barriers are enabled, as
- # they sometimes exhibit a deadlock issue with the concurrent
- # copying collector in the read barrier configuration, between the
- # HeapTaskDeamon and the JDWP thread (b/25800335).
- #
- # TODO: Re-enable the JDWP tests when this deadlock issue is fixed.
- echo "JDWP tests are temporarily disabled in the read barrier configuration because of"
- echo "a deadlock issue (b/25800335)."
- exit 0
-fi
-
art="/data/local/tmp/system/bin/art"
art_debugee="sh /data/local/tmp/system/bin/art"
args=$@