Improve GVN handling of ElementTransitions.
BUG= TEST= Review URL: https://chromiumcodereview.appspot.com/9141016 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10651 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
f833c4031b
commit
d949c64688
@ -893,6 +893,13 @@ void HCheckInstanceType::GetCheckMaskAndTag(uint8_t* mask, uint8_t* tag) {
|
|||||||
void HCheckMap::PrintDataTo(StringStream* stream) {
|
void HCheckMap::PrintDataTo(StringStream* stream) {
|
||||||
value()->PrintNameTo(stream);
|
value()->PrintNameTo(stream);
|
||||||
stream->Add(" %p", *map());
|
stream->Add(" %p", *map());
|
||||||
|
if (mode() == REQUIRE_EXACT_MAP) {
|
||||||
|
stream->Add(" [EXACT]");
|
||||||
|
} else if (!has_element_transitions_) {
|
||||||
|
stream->Add(" [EXACT*]");
|
||||||
|
} else {
|
||||||
|
stream->Add(" [MATCH ELEMENTS]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -186,6 +186,7 @@ class LChunkBuilder;
|
|||||||
V(InobjectFields) \
|
V(InobjectFields) \
|
||||||
V(BackingStoreFields) \
|
V(BackingStoreFields) \
|
||||||
V(ElementsKind) \
|
V(ElementsKind) \
|
||||||
|
V(ElementsPointer) \
|
||||||
V(ArrayElements) \
|
V(ArrayElements) \
|
||||||
V(DoubleArrayElements) \
|
V(DoubleArrayElements) \
|
||||||
V(SpecializedArrayElements) \
|
V(SpecializedArrayElements) \
|
||||||
@ -646,6 +647,18 @@ class HValue: public ZoneObject {
|
|||||||
return gvn_flags_.ContainsAnyOf(AllObservableSideEffectsFlagSet());
|
return gvn_flags_.ContainsAnyOf(AllObservableSideEffectsFlagSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GVNFlagSet DependsOnFlags() const {
|
||||||
|
GVNFlagSet result = gvn_flags_;
|
||||||
|
result.Intersect(AllDependsOnFlagSet());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
GVNFlagSet SideEffectFlags() const {
|
||||||
|
GVNFlagSet result = gvn_flags_;
|
||||||
|
result.Intersect(AllSideEffectsFlagSet());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
GVNFlagSet ChangesFlags() const {
|
GVNFlagSet ChangesFlags() const {
|
||||||
GVNFlagSet result = gvn_flags_;
|
GVNFlagSet result = gvn_flags_;
|
||||||
result.Intersect(AllChangesFlagSet());
|
result.Intersect(AllChangesFlagSet());
|
||||||
@ -722,6 +735,15 @@ class HValue: public ZoneObject {
|
|||||||
representation_ = r;
|
representation_ = r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GVNFlagSet AllDependsOnFlagSet() {
|
||||||
|
GVNFlagSet result;
|
||||||
|
// Create changes mask.
|
||||||
|
#define ADD_FLAG(type) result.Add(kDependsOn##type);
|
||||||
|
GVN_FLAG_LIST(ADD_FLAG)
|
||||||
|
#undef ADD_FLAG
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static GVNFlagSet AllChangesFlagSet() {
|
static GVNFlagSet AllChangesFlagSet() {
|
||||||
GVNFlagSet result;
|
GVNFlagSet result;
|
||||||
// Create changes mask.
|
// Create changes mask.
|
||||||
@ -743,6 +765,8 @@ class HValue: public ZoneObject {
|
|||||||
static GVNFlagSet AllObservableSideEffectsFlagSet() {
|
static GVNFlagSet AllObservableSideEffectsFlagSet() {
|
||||||
GVNFlagSet result = AllChangesFlagSet();
|
GVNFlagSet result = AllChangesFlagSet();
|
||||||
result.Remove(kChangesElementsKind);
|
result.Remove(kChangesElementsKind);
|
||||||
|
result.Remove(kChangesElementsPointer);
|
||||||
|
result.Remove(kChangesMaps);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1920,8 +1944,7 @@ class HLoadElements: public HUnaryOperation {
|
|||||||
explicit HLoadElements(HValue* value) : HUnaryOperation(value) {
|
explicit HLoadElements(HValue* value) : HUnaryOperation(value) {
|
||||||
set_representation(Representation::Tagged());
|
set_representation(Representation::Tagged());
|
||||||
SetFlag(kUseGVN);
|
SetFlag(kUseGVN);
|
||||||
SetGVNFlag(kDependsOnMaps);
|
SetGVNFlag(kDependsOnElementsPointer);
|
||||||
SetGVNFlag(kDependsOnElementsKind);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Representation RequiredInputRepresentation(int index) {
|
virtual Representation RequiredInputRepresentation(int index) {
|
||||||
@ -1972,6 +1995,11 @@ class HCheckMap: public HTemplateInstruction<2> {
|
|||||||
set_representation(Representation::Tagged());
|
set_representation(Representation::Tagged());
|
||||||
SetFlag(kUseGVN);
|
SetFlag(kUseGVN);
|
||||||
SetGVNFlag(kDependsOnMaps);
|
SetGVNFlag(kDependsOnMaps);
|
||||||
|
// If the map to check doesn't have the untransitioned elements, it must not
|
||||||
|
// be hoisted above TransitionElements instructions.
|
||||||
|
if (mode == REQUIRE_EXACT_MAP || !map->has_fast_smi_only_elements()) {
|
||||||
|
SetGVNFlag(kDependsOnElementsKind);
|
||||||
|
}
|
||||||
has_element_transitions_ =
|
has_element_transitions_ =
|
||||||
map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL) != NULL ||
|
map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL) != NULL ||
|
||||||
map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL) != NULL;
|
map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL) != NULL;
|
||||||
@ -4135,7 +4163,17 @@ class HTransitionElementsKind: public HTemplateInstruction<1> {
|
|||||||
transitioned_map_(transitioned_map) {
|
transitioned_map_(transitioned_map) {
|
||||||
SetOperandAt(0, object);
|
SetOperandAt(0, object);
|
||||||
SetFlag(kUseGVN);
|
SetFlag(kUseGVN);
|
||||||
|
SetGVNFlag(kDependsOnMaps);
|
||||||
SetGVNFlag(kChangesElementsKind);
|
SetGVNFlag(kChangesElementsKind);
|
||||||
|
if (original_map->has_fast_double_elements()) {
|
||||||
|
SetGVNFlag(kChangesElementsPointer);
|
||||||
|
SetGVNFlag(kDependsOnElementsPointer);
|
||||||
|
SetGVNFlag(kDependsOnDoubleArrayElements);
|
||||||
|
} else if (transitioned_map->has_fast_double_elements()) {
|
||||||
|
SetGVNFlag(kChangesElementsPointer);
|
||||||
|
SetGVNFlag(kDependsOnElementsPointer);
|
||||||
|
SetGVNFlag(kDependsOnArrayElements);
|
||||||
|
}
|
||||||
set_representation(Representation::Tagged());
|
set_representation(Representation::Tagged());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
163
src/hydrogen.cc
163
src/hydrogen.cc
@ -70,7 +70,8 @@ HBasicBlock::HBasicBlock(HGraph* graph)
|
|||||||
deleted_phis_(4),
|
deleted_phis_(4),
|
||||||
parent_loop_header_(NULL),
|
parent_loop_header_(NULL),
|
||||||
is_inline_return_target_(false),
|
is_inline_return_target_(false),
|
||||||
is_deoptimizing_(false) { }
|
is_deoptimizing_(false),
|
||||||
|
dominates_loop_successors_(false) { }
|
||||||
|
|
||||||
|
|
||||||
void HBasicBlock::AttachLoopInformation() {
|
void HBasicBlock::AttachLoopInformation() {
|
||||||
@ -315,6 +316,62 @@ void HBasicBlock::AssignCommonDominator(HBasicBlock* other) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void HBasicBlock::AssignLoopSuccessorDominators() {
|
||||||
|
// Mark blocks that dominate all subsequent reachable blocks inside their
|
||||||
|
// loop. Exploit the fact that blocks are sorted in reverse post order. When
|
||||||
|
// the loop is visited in increasing block id order, if the number of
|
||||||
|
// non-loop-exiting successor edges at the dominator_candidate block doesn't
|
||||||
|
// exceed the number of previously encountered predecessor edges, there is no
|
||||||
|
// path from the loop header to any block with higher id that doesn't go
|
||||||
|
// through the dominator_candidate block. In this case, the
|
||||||
|
// dominator_candidate block is guaranteed to dominate all blocks reachable
|
||||||
|
// from it with higher ids.
|
||||||
|
HBasicBlock* last = loop_information()->GetLastBackEdge();
|
||||||
|
int outstanding_successors = 1; // one edge from the pre-header
|
||||||
|
// Header always dominates everything.
|
||||||
|
MarkAsLoopSuccessorDominator();
|
||||||
|
for (int j = block_id(); j <= last->block_id(); ++j) {
|
||||||
|
HBasicBlock* dominator_candidate = graph_->blocks()->at(j);
|
||||||
|
for (HPredecessorIterator it(dominator_candidate); !it.Done();
|
||||||
|
it.Advance()) {
|
||||||
|
HBasicBlock* predecessor = it.Current();
|
||||||
|
// Don't count back edges.
|
||||||
|
if (predecessor->block_id() < dominator_candidate->block_id()) {
|
||||||
|
outstanding_successors--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If more successors than predecessors have been seen in the loop up to
|
||||||
|
// now, it's not possible to guarantee that the current block dominates
|
||||||
|
// all of the blocks with higher IDs. In this case, assume conservatively
|
||||||
|
// that those paths through loop that don't go through the current block
|
||||||
|
// contain all of the loop's dependencies. Also be careful to record
|
||||||
|
// dominator information about the current loop that's being processed,
|
||||||
|
// and not nested loops, which will be processed when
|
||||||
|
// AssignLoopSuccessorDominators gets called on their header.
|
||||||
|
ASSERT(outstanding_successors >= 0);
|
||||||
|
HBasicBlock* parent_loop_header = dominator_candidate->parent_loop_header();
|
||||||
|
if (outstanding_successors == 0 &&
|
||||||
|
(parent_loop_header == this && !dominator_candidate->IsLoopHeader())) {
|
||||||
|
dominator_candidate->MarkAsLoopSuccessorDominator();
|
||||||
|
}
|
||||||
|
HControlInstruction* end = dominator_candidate->end();
|
||||||
|
for (HSuccessorIterator it(end); !it.Done(); it.Advance()) {
|
||||||
|
HBasicBlock* successor = it.Current();
|
||||||
|
// Only count successors that remain inside the loop and don't loop back
|
||||||
|
// to a loop header.
|
||||||
|
if (successor->block_id() > dominator_candidate->block_id() &&
|
||||||
|
successor->block_id() <= last->block_id()) {
|
||||||
|
// Backwards edges must land on loop headers.
|
||||||
|
ASSERT(successor->block_id() > dominator_candidate->block_id() ||
|
||||||
|
successor->IsLoopHeader());
|
||||||
|
outstanding_successors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int HBasicBlock::PredecessorIndexOf(HBasicBlock* predecessor) const {
|
int HBasicBlock::PredecessorIndexOf(HBasicBlock* predecessor) const {
|
||||||
for (int i = 0; i < predecessors_.length(); ++i) {
|
for (int i = 0; i < predecessors_.length(); ++i) {
|
||||||
if (predecessors_[i] == predecessor) return i;
|
if (predecessors_[i] == predecessor) return i;
|
||||||
@ -750,10 +807,12 @@ void HGraph::Postorder(HBasicBlock* block,
|
|||||||
void HGraph::AssignDominators() {
|
void HGraph::AssignDominators() {
|
||||||
HPhase phase("Assign dominators", this);
|
HPhase phase("Assign dominators", this);
|
||||||
for (int i = 0; i < blocks_.length(); ++i) {
|
for (int i = 0; i < blocks_.length(); ++i) {
|
||||||
if (blocks_[i]->IsLoopHeader()) {
|
HBasicBlock* block = blocks_[i];
|
||||||
|
if (block->IsLoopHeader()) {
|
||||||
// Only the first predecessor of a loop header is from outside the loop.
|
// Only the first predecessor of a loop header is from outside the loop.
|
||||||
// All others are back edges, and thus cannot dominate the loop header.
|
// All others are back edges, and thus cannot dominate the loop header.
|
||||||
blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->first());
|
block->AssignCommonDominator(block->predecessors()->first());
|
||||||
|
block->AssignLoopSuccessorDominators();
|
||||||
} else {
|
} else {
|
||||||
for (int j = blocks_[i]->predecessors()->length() - 1; j >= 0; --j) {
|
for (int j = blocks_[i]->predecessors()->length() - 1; j >= 0; --j) {
|
||||||
blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j));
|
blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j));
|
||||||
@ -1371,7 +1430,8 @@ class HGlobalValueNumberer BASE_EMBEDDED {
|
|||||||
void LoopInvariantCodeMotion();
|
void LoopInvariantCodeMotion();
|
||||||
void ProcessLoopBlock(HBasicBlock* block,
|
void ProcessLoopBlock(HBasicBlock* block,
|
||||||
HBasicBlock* before_loop,
|
HBasicBlock* before_loop,
|
||||||
GVNFlagSet loop_kills);
|
GVNFlagSet loop_kills,
|
||||||
|
GVNFlagSet* accumulated_first_time_depends);
|
||||||
bool AllowCodeMotion();
|
bool AllowCodeMotion();
|
||||||
bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header);
|
bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header);
|
||||||
|
|
||||||
@ -1396,6 +1456,7 @@ class HGlobalValueNumberer BASE_EMBEDDED {
|
|||||||
|
|
||||||
|
|
||||||
bool HGlobalValueNumberer::Analyze() {
|
bool HGlobalValueNumberer::Analyze() {
|
||||||
|
removed_side_effects_ = false;
|
||||||
ComputeBlockSideEffects();
|
ComputeBlockSideEffects();
|
||||||
if (FLAG_loop_invariant_code_motion) {
|
if (FLAG_loop_invariant_code_motion) {
|
||||||
LoopInvariantCodeMotion();
|
LoopInvariantCodeMotion();
|
||||||
@ -1407,6 +1468,12 @@ bool HGlobalValueNumberer::Analyze() {
|
|||||||
|
|
||||||
|
|
||||||
void HGlobalValueNumberer::ComputeBlockSideEffects() {
|
void HGlobalValueNumberer::ComputeBlockSideEffects() {
|
||||||
|
// The Analyze phase of GVN can be called multiple times. Clear loop side
|
||||||
|
// effects before computing them to erase the contents from previous Analyze
|
||||||
|
// passes.
|
||||||
|
for (int i = 0; i < loop_side_effects_.length(); ++i) {
|
||||||
|
loop_side_effects_[i].RemoveAll();
|
||||||
|
}
|
||||||
for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
|
for (int i = graph_->blocks()->length() - 1; i >= 0; --i) {
|
||||||
// Compute side effects for the block.
|
// Compute side effects for the block.
|
||||||
HBasicBlock* block = graph_->blocks()->at(i);
|
HBasicBlock* block = graph_->blocks()->at(i);
|
||||||
@ -1444,18 +1511,22 @@ void HGlobalValueNumberer::LoopInvariantCodeMotion() {
|
|||||||
block->block_id(),
|
block->block_id(),
|
||||||
side_effects.ToIntegral());
|
side_effects.ToIntegral());
|
||||||
|
|
||||||
|
GVNFlagSet accumulated_first_time_depends;
|
||||||
HBasicBlock* last = block->loop_information()->GetLastBackEdge();
|
HBasicBlock* last = block->loop_information()->GetLastBackEdge();
|
||||||
for (int j = block->block_id(); j <= last->block_id(); ++j) {
|
for (int j = block->block_id(); j <= last->block_id(); ++j) {
|
||||||
ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects);
|
ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects,
|
||||||
|
&accumulated_first_time_depends);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void HGlobalValueNumberer::ProcessLoopBlock(HBasicBlock* block,
|
void HGlobalValueNumberer::ProcessLoopBlock(
|
||||||
HBasicBlock* loop_header,
|
HBasicBlock* block,
|
||||||
GVNFlagSet loop_kills) {
|
HBasicBlock* loop_header,
|
||||||
|
GVNFlagSet loop_kills,
|
||||||
|
GVNFlagSet* accumulated_first_time_depends) {
|
||||||
HBasicBlock* pre_header = loop_header->predecessors()->at(0);
|
HBasicBlock* pre_header = loop_header->predecessors()->at(0);
|
||||||
GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills);
|
GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills);
|
||||||
TraceGVN("Loop invariant motion for B%d depends_flags=0x%x\n",
|
TraceGVN("Loop invariant motion for B%d depends_flags=0x%x\n",
|
||||||
@ -1464,25 +1535,65 @@ void HGlobalValueNumberer::ProcessLoopBlock(HBasicBlock* block,
|
|||||||
HInstruction* instr = block->first();
|
HInstruction* instr = block->first();
|
||||||
while (instr != NULL) {
|
while (instr != NULL) {
|
||||||
HInstruction* next = instr->next();
|
HInstruction* next = instr->next();
|
||||||
if (instr->CheckFlag(HValue::kUseGVN) &&
|
bool hoisted = false;
|
||||||
!instr->gvn_flags().ContainsAnyOf(depends_flags)) {
|
if (instr->CheckFlag(HValue::kUseGVN)) {
|
||||||
TraceGVN("Checking instruction %d (%s)\n",
|
TraceGVN("Checking instruction %d (%s) instruction GVN flags 0x%X, "
|
||||||
|
"loop kills 0x%X\n",
|
||||||
instr->id(),
|
instr->id(),
|
||||||
instr->Mnemonic());
|
instr->Mnemonic(),
|
||||||
bool inputs_loop_invariant = true;
|
instr->gvn_flags().ToIntegral(),
|
||||||
for (int i = 0; i < instr->OperandCount(); ++i) {
|
depends_flags.ToIntegral());
|
||||||
if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) {
|
bool can_hoist = !instr->gvn_flags().ContainsAnyOf(depends_flags);
|
||||||
inputs_loop_invariant = false;
|
if (!can_hoist && instr->IsTransitionElementsKind()) {
|
||||||
}
|
// It's only possible to hoist one time side effects if there are no
|
||||||
|
// dependencies on their changes from the loop header to the current
|
||||||
|
// instruction.
|
||||||
|
GVNFlagSet converted_changes =
|
||||||
|
HValue::ConvertChangesToDependsFlags(instr->ChangesFlags());
|
||||||
|
TraceGVN("Checking dependencies on one-time instruction %d (%s) "
|
||||||
|
"converted changes 0x%X, accumulated depends 0x%X\n",
|
||||||
|
instr->id(),
|
||||||
|
instr->Mnemonic(),
|
||||||
|
converted_changes.ToIntegral(),
|
||||||
|
accumulated_first_time_depends->ToIntegral());
|
||||||
|
// It's possible to hoist one-time side effects from the current loop
|
||||||
|
// loop only if they dominate all of the successor blocks in the same
|
||||||
|
// loop and there are not any instructions that have Changes/DependsOn
|
||||||
|
// that intervene between it and the beginning of the loop header.
|
||||||
|
bool in_nested_loop = block != loop_header &&
|
||||||
|
((block->parent_loop_header() != loop_header) ||
|
||||||
|
block->IsLoopHeader());
|
||||||
|
can_hoist = !in_nested_loop &&
|
||||||
|
block->IsLoopSuccessorDominator() &&
|
||||||
|
!accumulated_first_time_depends->ContainsAnyOf(converted_changes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputs_loop_invariant && ShouldMove(instr, loop_header)) {
|
if (can_hoist) {
|
||||||
TraceGVN("Found loop invariant instruction %d\n", instr->id());
|
bool inputs_loop_invariant = true;
|
||||||
// Move the instruction out of the loop.
|
for (int i = 0; i < instr->OperandCount(); ++i) {
|
||||||
instr->Unlink();
|
if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) {
|
||||||
instr->InsertBefore(pre_header->end());
|
inputs_loop_invariant = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputs_loop_invariant && ShouldMove(instr, loop_header)) {
|
||||||
|
TraceGVN("Hoisting loop invariant instruction %d\n", instr->id());
|
||||||
|
// Move the instruction out of the loop.
|
||||||
|
instr->Unlink();
|
||||||
|
instr->InsertBefore(pre_header->end());
|
||||||
|
if (instr->HasSideEffects()) removed_side_effects_ = true;
|
||||||
|
hoisted = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!hoisted) {
|
||||||
|
// If an instruction is not hoisted, we have to account for its side
|
||||||
|
// effects when hoisting later HTransitionElementsKind instructions.
|
||||||
|
accumulated_first_time_depends->Add(instr->DependsOnFlags());
|
||||||
|
GVNFlagSet converted_changes =
|
||||||
|
HValue::ConvertChangesToDependsFlags(instr->SideEffectFlags());
|
||||||
|
accumulated_first_time_depends->Add(converted_changes);
|
||||||
|
}
|
||||||
instr = next;
|
instr = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2390,7 +2501,8 @@ HGraph* HGraphBuilder::CreateGraph() {
|
|||||||
// could only be discovered by removing side-effect-generating instructions
|
// could only be discovered by removing side-effect-generating instructions
|
||||||
// during the first pass.
|
// during the first pass.
|
||||||
if (FLAG_smi_only_arrays && removed_side_effects) {
|
if (FLAG_smi_only_arrays && removed_side_effects) {
|
||||||
gvn.Analyze();
|
removed_side_effects = gvn.Analyze();
|
||||||
|
ASSERT(!removed_side_effects);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7260,7 +7372,10 @@ void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PrintEmptyProperty("xhandlers");
|
PrintEmptyProperty("xhandlers");
|
||||||
PrintEmptyProperty("flags");
|
const char* flags = current->IsLoopSuccessorDominator()
|
||||||
|
? "dom-loop-succ"
|
||||||
|
: "";
|
||||||
|
PrintStringProperty("flags", flags);
|
||||||
|
|
||||||
if (current->dominator() != NULL) {
|
if (current->dominator() != NULL) {
|
||||||
PrintBlockProperty("dominator", current->dominator()->block_id());
|
PrintBlockProperty("dominator", current->dominator()->block_id());
|
||||||
|
@ -126,6 +126,7 @@ class HBasicBlock: public ZoneObject {
|
|||||||
int PredecessorIndexOf(HBasicBlock* predecessor) const;
|
int PredecessorIndexOf(HBasicBlock* predecessor) const;
|
||||||
void AddSimulate(int ast_id) { AddInstruction(CreateSimulate(ast_id)); }
|
void AddSimulate(int ast_id) { AddInstruction(CreateSimulate(ast_id)); }
|
||||||
void AssignCommonDominator(HBasicBlock* other);
|
void AssignCommonDominator(HBasicBlock* other);
|
||||||
|
void AssignLoopSuccessorDominators();
|
||||||
|
|
||||||
void FinishExitWithDeoptimization(HDeoptimize::UseEnvironment has_uses) {
|
void FinishExitWithDeoptimization(HDeoptimize::UseEnvironment has_uses) {
|
||||||
FinishExit(CreateDeoptimize(has_uses));
|
FinishExit(CreateDeoptimize(has_uses));
|
||||||
@ -149,6 +150,13 @@ class HBasicBlock: public ZoneObject {
|
|||||||
bool IsDeoptimizing() const { return is_deoptimizing_; }
|
bool IsDeoptimizing() const { return is_deoptimizing_; }
|
||||||
void MarkAsDeoptimizing() { is_deoptimizing_ = true; }
|
void MarkAsDeoptimizing() { is_deoptimizing_ = true; }
|
||||||
|
|
||||||
|
bool IsLoopSuccessorDominator() const {
|
||||||
|
return dominates_loop_successors_;
|
||||||
|
}
|
||||||
|
void MarkAsLoopSuccessorDominator() {
|
||||||
|
dominates_loop_successors_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
inline Zone* zone();
|
inline Zone* zone();
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@ -182,6 +190,22 @@ class HBasicBlock: public ZoneObject {
|
|||||||
HBasicBlock* parent_loop_header_;
|
HBasicBlock* parent_loop_header_;
|
||||||
bool is_inline_return_target_;
|
bool is_inline_return_target_;
|
||||||
bool is_deoptimizing_;
|
bool is_deoptimizing_;
|
||||||
|
bool dominates_loop_successors_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class HPredecessorIterator BASE_EMBEDDED {
|
||||||
|
public:
|
||||||
|
explicit HPredecessorIterator(HBasicBlock* block)
|
||||||
|
: predecessor_list_(block->predecessors()), current_(0) { }
|
||||||
|
|
||||||
|
bool Done() { return current_ >= predecessor_list_->length(); }
|
||||||
|
HBasicBlock* Current() { return predecessor_list_->at(current_); }
|
||||||
|
void Advance() { current_++; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const ZoneList<HBasicBlock*>* predecessor_list_;
|
||||||
|
int current_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -5511,7 +5511,7 @@ class PolymorphicCodeCacheHashTableKey : public HashTableKey {
|
|||||||
for (int i = 0; i < maps_->length(); ++i) {
|
for (int i = 0; i < maps_->length(); ++i) {
|
||||||
bool match_found = false;
|
bool match_found = false;
|
||||||
for (int j = 0; j < other_maps.length(); ++j) {
|
for (int j = 0; j < other_maps.length(); ++j) {
|
||||||
if (maps_->at(i)->EquivalentTo(*other_maps.at(j))) {
|
if (*(maps_->at(i)) == *(other_maps.at(j))) {
|
||||||
match_found = true;
|
match_found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -4696,12 +4696,6 @@ class Map: public HeapObject {
|
|||||||
// The "shared" flags of both this map and |other| are ignored.
|
// The "shared" flags of both this map and |other| are ignored.
|
||||||
bool EquivalentToForNormalization(Map* other, PropertyNormalizationMode mode);
|
bool EquivalentToForNormalization(Map* other, PropertyNormalizationMode mode);
|
||||||
|
|
||||||
// Returns true if this map and |other| describe equivalent objects.
|
|
||||||
// The "shared" flags of both this map and |other| are ignored.
|
|
||||||
bool EquivalentTo(Map* other) {
|
|
||||||
return EquivalentToForNormalization(other, KEEP_INOBJECT_PROPERTIES);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the contents of this map's descriptor array for the given string.
|
// Returns the contents of this map's descriptor array for the given string.
|
||||||
// May return NULL. |safe_to_add_transition| is set to false and NULL
|
// May return NULL. |safe_to_add_transition| is set to false and NULL
|
||||||
// is returned if adding transitions is not allowed.
|
// is returned if adding transitions is not allowed.
|
||||||
|
168
test/mjsunit/elements-transition-hoisting.js
Normal file
168
test/mjsunit/elements-transition-hoisting.js
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Flags: --allow-natives-syntax --smi-only-arrays
|
||||||
|
|
||||||
|
// Ensure that ElementsKind transitions in various situations are hoisted (or
|
||||||
|
// not hoisted) correctly, don't change the semantics programs and don't trigger
|
||||||
|
// deopt through hoisting in important situations.
|
||||||
|
|
||||||
|
support_smi_only_arrays = %HasFastSmiOnlyElements(new Array(1,2,3,4,5,6));
|
||||||
|
|
||||||
|
if (support_smi_only_arrays) {
|
||||||
|
print("Tests include smi-only arrays.");
|
||||||
|
} else {
|
||||||
|
print("Tests do NOT include smi-only arrays.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (support_smi_only_arrays) {
|
||||||
|
// Make sure that a simple elements array transitions inside a loop before
|
||||||
|
// stores to an array gets hoisted in a way that doesn't generate a deopt in
|
||||||
|
// simple cases.}
|
||||||
|
function testDoubleConversion4(a) {
|
||||||
|
var object = new Object();
|
||||||
|
a[0] = 0;
|
||||||
|
var count = 3;
|
||||||
|
do {
|
||||||
|
a[0] = object;
|
||||||
|
} while (--count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
testDoubleConversion4(new Array(5));
|
||||||
|
%OptimizeFunctionOnNextCall(testDoubleConversion4);
|
||||||
|
testDoubleConversion4(new Array(5));
|
||||||
|
testDoubleConversion4(new Array(5));
|
||||||
|
assertTrue(2 != %GetOptimizationStatus(testDoubleConversion4));
|
||||||
|
|
||||||
|
// Make sure that non-element related map checks that are not preceded by
|
||||||
|
// transitions in a loop still get hoisted in a way that doesn't generate a
|
||||||
|
// deopt in simple cases.
|
||||||
|
function testExactMapHoisting(a) {
|
||||||
|
var object = new Object();
|
||||||
|
a.foo = 0;
|
||||||
|
a[0] = 0;
|
||||||
|
a[1] = 1;
|
||||||
|
var count = 3;
|
||||||
|
do {
|
||||||
|
a.foo = object; // This map check should be hoistable
|
||||||
|
a[1] = object;
|
||||||
|
result = a.foo == object && a[1] == object;
|
||||||
|
} while (--count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
testExactMapHoisting(new Array(5));
|
||||||
|
%OptimizeFunctionOnNextCall(testExactMapHoisting);
|
||||||
|
testExactMapHoisting(new Array(5));
|
||||||
|
testExactMapHoisting(new Array(5));
|
||||||
|
assertTrue(2 != %GetOptimizationStatus(testExactMapHoisting));
|
||||||
|
|
||||||
|
// Make sure that non-element related map checks do NOT get hoisted if they
|
||||||
|
// depend on an elements transition before them and it's not possible to hoist
|
||||||
|
// that transition.
|
||||||
|
function testExactMapHoisting2(a) {
|
||||||
|
var object = new Object();
|
||||||
|
a.foo = 0;
|
||||||
|
a[0] = 0;
|
||||||
|
a[1] = 1;
|
||||||
|
var count = 3;
|
||||||
|
do {
|
||||||
|
if (a.bar === undefined) {
|
||||||
|
a[1] = 2.5;
|
||||||
|
}
|
||||||
|
a.foo = object; // This map check should NOT be hoistable because it
|
||||||
|
// includes a check for the FAST_ELEMENTS map as well as
|
||||||
|
// the FAST_DOUBLE_ELEMENTS map, which depends on the
|
||||||
|
// double transition above in the if, which cannot be
|
||||||
|
// hoisted.
|
||||||
|
} while (--count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
testExactMapHoisting2(new Array(5));
|
||||||
|
%OptimizeFunctionOnNextCall(testExactMapHoisting2);
|
||||||
|
testExactMapHoisting2(new Array(5));
|
||||||
|
testExactMapHoisting2(new Array(5));
|
||||||
|
assertTrue(2 != %GetOptimizationStatus(testExactMapHoisting2));
|
||||||
|
|
||||||
|
// Make sure that non-element related map checks do get hoisted if they use
|
||||||
|
// the transitioned map for the check and all transitions that they depend
|
||||||
|
// upon can hoisted, too.
|
||||||
|
function testExactMapHoisting3(a) {
|
||||||
|
var object = new Object();
|
||||||
|
a.foo = 0;
|
||||||
|
a[0] = 0;
|
||||||
|
a[1] = 1;
|
||||||
|
var count = 3;
|
||||||
|
do {
|
||||||
|
a[1] = 2.5;
|
||||||
|
a.foo = object; // This map check should be hoistable because all elements
|
||||||
|
// transitions in the loop can also be hoisted.
|
||||||
|
} while (--count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var add_transition = new Array(5);
|
||||||
|
add_transition.foo = 0;
|
||||||
|
add_transition[0] = new Object(); // For FAST_ELEMENT transition to be created
|
||||||
|
testExactMapHoisting3(new Array(5));
|
||||||
|
%OptimizeFunctionOnNextCall(testExactMapHoisting3);
|
||||||
|
testExactMapHoisting3(new Array(5));
|
||||||
|
testExactMapHoisting3(new Array(5));
|
||||||
|
assertTrue(2 != %GetOptimizationStatus(testExactMapHoisting3));
|
||||||
|
|
||||||
|
function testDominatingTransitionHoisting1(a) {
|
||||||
|
var object = new Object();
|
||||||
|
a[0] = 0;
|
||||||
|
var count = 3;
|
||||||
|
do {
|
||||||
|
if (a.baz != true) {
|
||||||
|
a[1] = 2.5;
|
||||||
|
}
|
||||||
|
a[0] = object;
|
||||||
|
} while (--count > 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
testDominatingTransitionHoisting1(new Array(5));
|
||||||
|
%OptimizeFunctionOnNextCall(testDominatingTransitionHoisting1);
|
||||||
|
testDominatingTransitionHoisting1(new Array(5));
|
||||||
|
testDominatingTransitionHoisting1(new Array(5));
|
||||||
|
assertTrue(2 != %GetOptimizationStatus(testDominatingTransitionHoisting1));
|
||||||
|
|
||||||
|
function testHoistingWithSideEffect(a) {
|
||||||
|
var object = new Object();
|
||||||
|
a[0] = 0;
|
||||||
|
var count = 3;
|
||||||
|
do {
|
||||||
|
assertTrue(true);
|
||||||
|
a[0] = object;
|
||||||
|
} while (--count > 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
testHoistingWithSideEffect(new Array(5));
|
||||||
|
%OptimizeFunctionOnNextCall(testHoistingWithSideEffect);
|
||||||
|
testHoistingWithSideEffect(new Array(5));
|
||||||
|
testHoistingWithSideEffect(new Array(5));
|
||||||
|
assertTrue(2 != %GetOptimizationStatus(testHoistingWithSideEffect));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user