diff --git a/src/compiler/escape-analysis-reducer.cc b/src/compiler/escape-analysis-reducer.cc index 59ad39bd42..22ece9d24b 100644 --- a/src/compiler/escape-analysis-reducer.cc +++ b/src/compiler/escape-analysis-reducer.cc @@ -46,6 +46,8 @@ Reduction EscapeAnalysisReducer::ReduceNode(Node* node) { case IrOpcode::kStoreField: case IrOpcode::kStoreElement: return ReduceStore(node); + case IrOpcode::kCheckMaps: + return ReduceCheckMaps(node); case IrOpcode::kAllocate: return ReduceAllocate(node); case IrOpcode::kFinishRegion: @@ -171,6 +173,21 @@ Reduction EscapeAnalysisReducer::ReduceStore(Node* node) { return NoChange(); } +Reduction EscapeAnalysisReducer::ReduceCheckMaps(Node* node) { + DCHECK(node->opcode() == IrOpcode::kCheckMaps); + if (node->id() < static_cast(fully_reduced_.length())) { + fully_reduced_.Add(node->id()); + } + if (escape_analysis()->IsVirtual( + SkipTypeGuards(NodeProperties::GetValueInput(node, 0))) && + !escape_analysis()->IsEscaped(node)) { + TRACE("Removed #%d (%s) from effect chain\n", node->id(), + node->op()->mnemonic()); + RelaxEffectsAndControls(node); + return Changed(node); + } + return NoChange(); +} Reduction EscapeAnalysisReducer::ReduceAllocate(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kAllocate); diff --git a/src/compiler/escape-analysis-reducer.h b/src/compiler/escape-analysis-reducer.h index b91082f1af..4373fa4c66 100644 --- a/src/compiler/escape-analysis-reducer.h +++ b/src/compiler/escape-analysis-reducer.h @@ -38,6 +38,7 @@ class V8_EXPORT_PRIVATE EscapeAnalysisReducer final Reduction ReduceNode(Node* node); Reduction ReduceLoad(Node* node); Reduction ReduceStore(Node* node); + Reduction ReduceCheckMaps(Node* node); Reduction ReduceAllocate(Node* node); Reduction ReduceFinishRegion(Node* node); Reduction ReduceReferenceEqual(Node* node); diff --git a/src/compiler/escape-analysis.cc b/src/compiler/escape-analysis.cc index 867afd6201..2e5e8bf012 100644 --- a/src/compiler/escape-analysis.cc +++ b/src/compiler/escape-analysis.cc @@ -807,6 +807,7 @@ bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep, case IrOpcode::kStateValues: case IrOpcode::kReferenceEqual: case IrOpcode::kFinishRegion: + case IrOpcode::kCheckMaps: if (IsEscaped(use) && SetEscaped(rep)) { TRACE( "Setting #%d (%s) to escaped because of use by escaping node " @@ -1124,6 +1125,9 @@ bool EscapeAnalysis::Process(Node* node) { case IrOpcode::kLoadElement: ProcessLoadElement(node); break; + case IrOpcode::kCheckMaps: + ProcessCheckMaps(node); + break; case IrOpcode::kStart: ProcessStart(node); break; @@ -1161,6 +1165,10 @@ void EscapeAnalysis::ProcessAllocationUsers(Node* node) { case IrOpcode::kFinishRegion: case IrOpcode::kObjectIsSmi: break; + case IrOpcode::kCheckMaps: { + CheckMapsParameters params = CheckMapsParametersOf(node->op()); + if (params.flags() == CheckMapsFlag::kNone) break; + } // Fallthrough. default: VirtualState* state = virtual_states_[node->id()]; if (VirtualObject* obj = @@ -1514,6 +1522,46 @@ void EscapeAnalysis::ProcessLoadField(Node* node) { } } +void EscapeAnalysis::ProcessCheckMaps(Node* node) { + DCHECK_EQ(node->opcode(), IrOpcode::kCheckMaps); + ForwardVirtualState(node); + Node* checked = ResolveReplacement(NodeProperties::GetValueInput(node, 0)); + if (FLAG_turbo_experimental) { + VirtualState* state = virtual_states_[node->id()]; + if (VirtualObject* object = GetVirtualObject(state, checked)) { + if (!object->IsTracked()) { + if (status_analysis_->SetEscaped(node)) { + TRACE( + "Setting #%d (%s) to escaped because checked object #%i is not " + "tracked\n", + node->id(), node->op()->mnemonic(), object->id()); + } + return; + } + CheckMapsParameters params = CheckMapsParametersOf(node->op()); + + Node* value = object->GetField(HeapObject::kMapOffset / kPointerSize); + if (value) { + value = ResolveReplacement(value); + // TODO(tebbi): We want to extend this beyond constant folding with a + // CheckMapsValue operator that takes the load-eliminated map value as + // input. + if (value->opcode() == IrOpcode::kHeapConstant && + params.maps().contains(ZoneHandleSet( + Handle::cast(OpParameter>(value))))) { + TRACE("CheckMaps #%i seems to be redundant (until now).\n", + node->id()); + return; + } + } + } + } + if (status_analysis_->SetEscaped(node)) { + TRACE("Setting #%d (%s) to escaped (checking #%i)\n", node->id(), + node->op()->mnemonic(), checked->id()); + } +} + void EscapeAnalysis::ProcessLoadElement(Node* node) { DCHECK_EQ(node->opcode(), IrOpcode::kLoadElement); ForwardVirtualState(node); diff --git a/src/compiler/escape-analysis.h b/src/compiler/escape-analysis.h index 52edc4be0b..87b4beed32 100644 --- a/src/compiler/escape-analysis.h +++ b/src/compiler/escape-analysis.h @@ -45,6 +45,7 @@ class V8_EXPORT_PRIVATE EscapeAnalysis { void ProcessStoreField(Node* node); void ProcessLoadElement(Node* node); void ProcessStoreElement(Node* node); + void ProcessCheckMaps(Node* node); void ProcessAllocationUsers(Node* node); void ProcessAllocation(Node* node); void ProcessFinishRegion(Node* node); diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 48c83ea8c4..0dcd9858ca 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -499,6 +499,8 @@ DEFINE_BOOL(turbo_stress_instruction_scheduling, false, "randomly schedule instructions to stress dependency tracking") DEFINE_BOOL(turbo_store_elimination, true, "enable store-store elimination in TurboFan") +DEFINE_BOOL(turbo_experimental, false, + "enable crashing features, for testing purposes only") // TODO(turbofan): Rename --crankshaft to --optimize eventually. DEFINE_IMPLICATION(turbo, crankshaft) diff --git a/test/mjsunit/compiler/escape-analysis-13.js b/test/mjsunit/compiler/escape-analysis-13.js new file mode 100644 index 0000000000..b1334f1f27 --- /dev/null +++ b/test/mjsunit/compiler/escape-analysis-13.js @@ -0,0 +1,17 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax --turbo --turbo-escape --turbo-experimental + +function f() { + var x = {}; + x.a = "a"; + x.b = "b"; + assertEquals("a", x.a); + assertEquals("b", x.b); +} +f(); +f(); +% OptimizeFunctionOnNextCall(f); +f();