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) {
|
||||
value()->PrintNameTo(stream);
|
||||
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(BackingStoreFields) \
|
||||
V(ElementsKind) \
|
||||
V(ElementsPointer) \
|
||||
V(ArrayElements) \
|
||||
V(DoubleArrayElements) \
|
||||
V(SpecializedArrayElements) \
|
||||
@ -646,6 +647,18 @@ class HValue: public ZoneObject {
|
||||
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 result = gvn_flags_;
|
||||
result.Intersect(AllChangesFlagSet());
|
||||
@ -722,6 +735,15 @@ class HValue: public ZoneObject {
|
||||
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() {
|
||||
GVNFlagSet result;
|
||||
// Create changes mask.
|
||||
@ -743,6 +765,8 @@ class HValue: public ZoneObject {
|
||||
static GVNFlagSet AllObservableSideEffectsFlagSet() {
|
||||
GVNFlagSet result = AllChangesFlagSet();
|
||||
result.Remove(kChangesElementsKind);
|
||||
result.Remove(kChangesElementsPointer);
|
||||
result.Remove(kChangesMaps);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1920,8 +1944,7 @@ class HLoadElements: public HUnaryOperation {
|
||||
explicit HLoadElements(HValue* value) : HUnaryOperation(value) {
|
||||
set_representation(Representation::Tagged());
|
||||
SetFlag(kUseGVN);
|
||||
SetGVNFlag(kDependsOnMaps);
|
||||
SetGVNFlag(kDependsOnElementsKind);
|
||||
SetGVNFlag(kDependsOnElementsPointer);
|
||||
}
|
||||
|
||||
virtual Representation RequiredInputRepresentation(int index) {
|
||||
@ -1972,6 +1995,11 @@ class HCheckMap: public HTemplateInstruction<2> {
|
||||
set_representation(Representation::Tagged());
|
||||
SetFlag(kUseGVN);
|
||||
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_ =
|
||||
map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL) != NULL ||
|
||||
map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL) != NULL;
|
||||
@ -4135,7 +4163,17 @@ class HTransitionElementsKind: public HTemplateInstruction<1> {
|
||||
transitioned_map_(transitioned_map) {
|
||||
SetOperandAt(0, object);
|
||||
SetFlag(kUseGVN);
|
||||
SetGVNFlag(kDependsOnMaps);
|
||||
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());
|
||||
}
|
||||
|
||||
|
163
src/hydrogen.cc
163
src/hydrogen.cc
@ -70,7 +70,8 @@ HBasicBlock::HBasicBlock(HGraph* graph)
|
||||
deleted_phis_(4),
|
||||
parent_loop_header_(NULL),
|
||||
is_inline_return_target_(false),
|
||||
is_deoptimizing_(false) { }
|
||||
is_deoptimizing_(false),
|
||||
dominates_loop_successors_(false) { }
|
||||
|
||||
|
||||
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 {
|
||||
for (int i = 0; i < predecessors_.length(); ++i) {
|
||||
if (predecessors_[i] == predecessor) return i;
|
||||
@ -750,10 +807,12 @@ void HGraph::Postorder(HBasicBlock* block,
|
||||
void HGraph::AssignDominators() {
|
||||
HPhase phase("Assign dominators", this);
|
||||
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.
|
||||
// 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 {
|
||||
for (int j = blocks_[i]->predecessors()->length() - 1; j >= 0; --j) {
|
||||
blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j));
|
||||
@ -1371,7 +1430,8 @@ class HGlobalValueNumberer BASE_EMBEDDED {
|
||||
void LoopInvariantCodeMotion();
|
||||
void ProcessLoopBlock(HBasicBlock* block,
|
||||
HBasicBlock* before_loop,
|
||||
GVNFlagSet loop_kills);
|
||||
GVNFlagSet loop_kills,
|
||||
GVNFlagSet* accumulated_first_time_depends);
|
||||
bool AllowCodeMotion();
|
||||
bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header);
|
||||
|
||||
@ -1396,6 +1456,7 @@ class HGlobalValueNumberer BASE_EMBEDDED {
|
||||
|
||||
|
||||
bool HGlobalValueNumberer::Analyze() {
|
||||
removed_side_effects_ = false;
|
||||
ComputeBlockSideEffects();
|
||||
if (FLAG_loop_invariant_code_motion) {
|
||||
LoopInvariantCodeMotion();
|
||||
@ -1407,6 +1468,12 @@ bool HGlobalValueNumberer::Analyze() {
|
||||
|
||||
|
||||
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) {
|
||||
// Compute side effects for the block.
|
||||
HBasicBlock* block = graph_->blocks()->at(i);
|
||||
@ -1444,18 +1511,22 @@ void HGlobalValueNumberer::LoopInvariantCodeMotion() {
|
||||
block->block_id(),
|
||||
side_effects.ToIntegral());
|
||||
|
||||
GVNFlagSet accumulated_first_time_depends;
|
||||
HBasicBlock* last = block->loop_information()->GetLastBackEdge();
|
||||
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,
|
||||
HBasicBlock* loop_header,
|
||||
GVNFlagSet loop_kills) {
|
||||
void HGlobalValueNumberer::ProcessLoopBlock(
|
||||
HBasicBlock* block,
|
||||
HBasicBlock* loop_header,
|
||||
GVNFlagSet loop_kills,
|
||||
GVNFlagSet* accumulated_first_time_depends) {
|
||||
HBasicBlock* pre_header = loop_header->predecessors()->at(0);
|
||||
GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills);
|
||||
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();
|
||||
while (instr != NULL) {
|
||||
HInstruction* next = instr->next();
|
||||
if (instr->CheckFlag(HValue::kUseGVN) &&
|
||||
!instr->gvn_flags().ContainsAnyOf(depends_flags)) {
|
||||
TraceGVN("Checking instruction %d (%s)\n",
|
||||
bool hoisted = false;
|
||||
if (instr->CheckFlag(HValue::kUseGVN)) {
|
||||
TraceGVN("Checking instruction %d (%s) instruction GVN flags 0x%X, "
|
||||
"loop kills 0x%X\n",
|
||||
instr->id(),
|
||||
instr->Mnemonic());
|
||||
bool inputs_loop_invariant = true;
|
||||
for (int i = 0; i < instr->OperandCount(); ++i) {
|
||||
if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) {
|
||||
inputs_loop_invariant = false;
|
||||
}
|
||||
instr->Mnemonic(),
|
||||
instr->gvn_flags().ToIntegral(),
|
||||
depends_flags.ToIntegral());
|
||||
bool can_hoist = !instr->gvn_flags().ContainsAnyOf(depends_flags);
|
||||
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)) {
|
||||
TraceGVN("Found loop invariant instruction %d\n", instr->id());
|
||||
// Move the instruction out of the loop.
|
||||
instr->Unlink();
|
||||
instr->InsertBefore(pre_header->end());
|
||||
if (can_hoist) {
|
||||
bool inputs_loop_invariant = true;
|
||||
for (int i = 0; i < instr->OperandCount(); ++i) {
|
||||
if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -2390,7 +2501,8 @@ HGraph* HGraphBuilder::CreateGraph() {
|
||||
// could only be discovered by removing side-effect-generating instructions
|
||||
// during the first pass.
|
||||
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("flags");
|
||||
const char* flags = current->IsLoopSuccessorDominator()
|
||||
? "dom-loop-succ"
|
||||
: "";
|
||||
PrintStringProperty("flags", flags);
|
||||
|
||||
if (current->dominator() != NULL) {
|
||||
PrintBlockProperty("dominator", current->dominator()->block_id());
|
||||
|
@ -126,6 +126,7 @@ class HBasicBlock: public ZoneObject {
|
||||
int PredecessorIndexOf(HBasicBlock* predecessor) const;
|
||||
void AddSimulate(int ast_id) { AddInstruction(CreateSimulate(ast_id)); }
|
||||
void AssignCommonDominator(HBasicBlock* other);
|
||||
void AssignLoopSuccessorDominators();
|
||||
|
||||
void FinishExitWithDeoptimization(HDeoptimize::UseEnvironment has_uses) {
|
||||
FinishExit(CreateDeoptimize(has_uses));
|
||||
@ -149,6 +150,13 @@ class HBasicBlock: public ZoneObject {
|
||||
bool IsDeoptimizing() const { return is_deoptimizing_; }
|
||||
void MarkAsDeoptimizing() { is_deoptimizing_ = true; }
|
||||
|
||||
bool IsLoopSuccessorDominator() const {
|
||||
return dominates_loop_successors_;
|
||||
}
|
||||
void MarkAsLoopSuccessorDominator() {
|
||||
dominates_loop_successors_ = true;
|
||||
}
|
||||
|
||||
inline Zone* zone();
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -182,6 +190,22 @@ class HBasicBlock: public ZoneObject {
|
||||
HBasicBlock* parent_loop_header_;
|
||||
bool is_inline_return_target_;
|
||||
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) {
|
||||
bool match_found = false;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
@ -4696,12 +4696,6 @@ class Map: public HeapObject {
|
||||
// The "shared" flags of both this map and |other| are ignored.
|
||||
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.
|
||||
// May return NULL. |safe_to_add_transition| is set to false and NULL
|
||||
// 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