Commit aa0ddf7d authored by sigurds's avatar sigurds Committed by Commit bot

[turbofan] Initial support for escape analysis.

This is the first part of escape analysis for turbofan.
At the moment, there is no deopt support, and support
for loops is partial (only binary Phis are handled).

The CL includes 4 unittests.

There are also 8 new mjsunit tests, some of which are
skiped as they require features not yet implemented.

BUG=v8:4586
LOG=n

Review URL: https://codereview.chromium.org/1457683003

Cr-Commit-Position: refs/heads/master@{#32498}
parent 9bee6750
...@@ -760,6 +760,10 @@ source_set("v8_base") { ...@@ -760,6 +760,10 @@ source_set("v8_base") {
"src/compiler/dead-code-elimination.cc", "src/compiler/dead-code-elimination.cc",
"src/compiler/dead-code-elimination.h", "src/compiler/dead-code-elimination.h",
"src/compiler/diamond.h", "src/compiler/diamond.h",
"src/compiler/escape-analysis.cc",
"src/compiler/escape-analysis.h",
"src/compiler/escape-analysis-reducer.cc",
"src/compiler/escape-analysis-reducer.h",
"src/compiler/frame.cc", "src/compiler/frame.cc",
"src/compiler/frame.h", "src/compiler/frame.h",
"src/compiler/frame-elider.cc", "src/compiler/frame-elider.cc",
......
// Copyright 2015 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.
#include "src/compiler/escape-analysis-reducer.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h"
namespace v8 {
namespace internal {
namespace compiler {
EscapeAnalysisReducer::EscapeAnalysisReducer(
Editor* editor, JSGraph* jsgraph, EscapeStatusAnalysis* escape_status,
EscapeObjectAnalysis* escape_analysis, Zone* zone)
: AdvancedReducer(editor),
jsgraph_(jsgraph),
escape_status_(escape_status),
escape_analysis_(escape_analysis),
zone_(zone) {}
Reduction EscapeAnalysisReducer::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kLoadField:
return ReduceLoadField(node);
case IrOpcode::kStoreField:
return ReduceStoreField(node);
case IrOpcode::kAllocate:
return ReduceAllocate(node);
case IrOpcode::kFinishRegion:
return ReduceFinishRegion(node);
case IrOpcode::kReferenceEqual:
return ReduceReferenceEqual(node);
case IrOpcode::kStateValues:
case IrOpcode::kFrameState:
return ReplaceWithDeoptDummy(node);
default:
break;
}
return NoChange();
}
Reduction EscapeAnalysisReducer::ReduceLoadField(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kLoadField);
if (Node* rep = escape_analysis()->GetReplacement(node, node->id())) {
if (FLAG_trace_turbo_escape) {
PrintF("Replaced #%d with #%d\n", node->id(), rep->id());
}
ReplaceWithValue(node, rep);
return Changed(rep);
}
return NoChange();
}
Reduction EscapeAnalysisReducer::ReduceStoreField(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kStoreField);
if (escape_status()->IsVirtual(NodeProperties::GetValueInput(node, 0))) {
if (FLAG_trace_turbo_escape) {
PrintF("Removed store field #%d from effect chain\n", node->id());
}
RelaxEffectsAndControls(node);
return Changed(node);
}
return NoChange();
}
Reduction EscapeAnalysisReducer::ReduceAllocate(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kAllocate);
if (escape_status()->IsVirtual(node)) {
RelaxEffectsAndControls(node);
if (FLAG_trace_turbo_escape) {
PrintF("Removed allocate #%d from effect chain\n", node->id());
}
return Changed(node);
}
return NoChange();
}
Reduction EscapeAnalysisReducer::ReduceFinishRegion(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion);
Node* effect = NodeProperties::GetEffectInput(node, 0);
if (effect->opcode() == IrOpcode::kBeginRegion) {
RelaxEffectsAndControls(effect);
RelaxEffectsAndControls(node);
if (FLAG_trace_turbo_escape) {
PrintF("Removed region #%d / #%d from effect chain,", effect->id(),
node->id());
PrintF("%d user(s) of #%d remain(s):", node->UseCount(), node->id());
for (Edge edge : node->use_edges()) {
PrintF(" #%d", edge.from()->id());
}
PrintF("\n");
}
return Changed(node);
}
return NoChange();
}
Reduction EscapeAnalysisReducer::ReduceReferenceEqual(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kReferenceEqual);
Node* left = NodeProperties::GetValueInput(node, 0);
Node* right = NodeProperties::GetValueInput(node, 1);
if (escape_status()->IsVirtual(left)) {
if (escape_status()->IsVirtual(right)) {
if (Node* rep = escape_analysis()->GetReplacement(node, left->id())) {
left = rep;
}
if (Node* rep = escape_analysis()->GetReplacement(node, right->id())) {
right = rep;
}
// TODO(sigurds): What to do if either is a PHI?
if (left == right) {
ReplaceWithValue(node, jsgraph()->TrueConstant());
if (FLAG_trace_turbo_escape) {
PrintF("Replaced ref eq #%d with true\n", node->id());
}
return Replace(node);
}
}
// Right-hand side is either not virtual, or a different node.
ReplaceWithValue(node, jsgraph()->FalseConstant());
if (FLAG_trace_turbo_escape) {
PrintF("Replaced ref eq #%d with false\n", node->id());
}
return Replace(node);
} else if (escape_status()->IsVirtual(right)) {
// Left-hand side is not a virtual object.
ReplaceWithValue(node, jsgraph()->FalseConstant());
if (FLAG_trace_turbo_escape) {
PrintF("Replaced ref eq #%d with false\n", node->id());
}
}
return NoChange();
}
// TODO(sigurds): This is a temporary solution until escape analysis
// supports deoptimization.
Reduction EscapeAnalysisReducer::ReplaceWithDeoptDummy(Node* node) {
DCHECK(node->opcode() == IrOpcode::kStateValues ||
node->opcode() == IrOpcode::kFrameState);
Reduction r = NoChange();
for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
Node* input = NodeProperties::GetValueInput(node, i);
if (input->opcode() == IrOpcode::kFinishRegion ||
input->opcode() == IrOpcode::kAllocate ||
input->opcode() == IrOpcode::kPhi) {
if (escape_status()->IsVirtual(input)) {
NodeProperties::ReplaceValueInput(node, jsgraph()->UndefinedConstant(),
i);
if (FLAG_trace_turbo_escape) {
PrintF("Replaced state value (#%d) input with dummy\n", node->id());
}
r = Changed(node);
}
}
}
return r;
}
} // namespace compiler
} // namespace internal
} // namespace v8
// Copyright 2015 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.
#ifndef V8_COMPILER_ESCAPE_ANALYSIS_REDUCER_H_
#define V8_COMPILER_ESCAPE_ANALYSIS_REDUCER_H_
#include "src/compiler/escape-analysis.h"
#include "src/compiler/graph-reducer.h"
namespace v8 {
namespace internal {
namespace compiler {
// Forward declarations.
class JSGraph;
class EscapeAnalysisReducer final : public AdvancedReducer {
public:
EscapeAnalysisReducer(Editor* editor, JSGraph* jsgraph,
EscapeStatusAnalysis* escape_status,
EscapeObjectAnalysis* escape_analysis, Zone* zone);
Reduction Reduce(Node* node) final;
private:
Reduction ReduceLoadField(Node* node);
Reduction ReduceStoreField(Node* node);
Reduction ReduceAllocate(Node* node);
Reduction ReduceFinishRegion(Node* node);
Reduction ReduceReferenceEqual(Node* node);
Reduction ReplaceWithDeoptDummy(Node* node);
JSGraph* jsgraph() const { return jsgraph_; }
EscapeObjectAnalysis* escape_analysis() const { return escape_analysis_; }
EscapeStatusAnalysis* escape_status() const { return escape_status_; }
Zone* zone() const { return zone_; }
JSGraph* const jsgraph_;
EscapeStatusAnalysis* escape_status_;
EscapeObjectAnalysis* escape_analysis_;
Zone* const zone_;
DISALLOW_COPY_AND_ASSIGN(EscapeAnalysisReducer);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_ESCAPE_ANALYSIS_REDUCER_H_
// Copyright 2015 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.
#include "src/compiler/escape-analysis.h"
#include "src/base/flags.h"
#include "src/bootstrapper.h"
#include "src/compilation-dependencies.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/node.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/simplified-operator.h"
#include "src/objects-inl.h"
#include "src/type-cache.h"
namespace v8 {
namespace internal {
namespace compiler {
class VirtualObject : public ZoneObject {
public:
enum Status { kUntracked = 0, kTracked = 1 };
VirtualObject(NodeId id, Zone* zone)
: id_(id), status_(kUntracked), fields_(zone), replacement_(nullptr) {}
VirtualObject(const VirtualObject& other)
: id_(other.id_),
status_(other.status_),
fields_(other.fields_),
replacement_(other.replacement_) {}
VirtualObject(NodeId id, Zone* zone, size_t field_number)
: id_(id), status_(kTracked), fields_(zone), replacement_(nullptr) {
fields_.resize(field_number);
}
Node* GetField(size_t offset) {
if (offset < fields_.size()) {
return fields_[offset];
}
return nullptr;
}
bool SetField(size_t offset, Node* node) {
bool changed = fields_[offset] != node;
fields_[offset] = node;
return changed;
}
bool IsVirtual() const { return status_ == kTracked; }
bool IsTracked() const { return status_ != kUntracked; }
Node* GetReplacement() { return replacement_; }
bool SetReplacement(Node* node) {
bool changed = replacement_ != node;
replacement_ = node;
return changed;
}
size_t fields() { return fields_.size(); }
bool ResizeFields(size_t field_number) {
if (field_number != fields_.size()) {
fields_.resize(field_number);
return true;
}
return false;
}
bool UpdateFrom(const VirtualObject& other);
NodeId id() { return id_; }
void id(NodeId id) { id_ = id; }
private:
NodeId id_;
Status status_;
ZoneVector<Node*> fields_;
Node* replacement_;
};
class VirtualState : public ZoneObject {
public:
VirtualState(Zone* zone, size_t size);
VirtualState(const VirtualState& states);
VirtualObject* GetVirtualObject(Node* node);
VirtualObject* GetVirtualObject(size_t id);
VirtualObject* GetOrCreateTrackedVirtualObject(NodeId id, Zone* zone);
void SetVirtualObject(NodeId id, VirtualObject* state);
void LastChangedAt(Node* node) { last_changed_ = node; }
Node* GetLastChanged() { return last_changed_; }
bool UpdateFrom(NodeId id, VirtualObject* state, Zone* zone);
Node* ResolveReplacement(Node* node);
bool UpdateReplacement(Node* node, Node* rep, Zone* zone);
bool UpdateFrom(VirtualState* state, Zone* zone);
bool MergeFrom(VirtualState* left, VirtualState* right, Zone* zone,
Graph* graph, CommonOperatorBuilder* common, Node* control);
size_t size() { return info_.size(); }
private:
ZoneVector<VirtualObject*> info_;
Node* last_changed_;
};
bool VirtualObject::UpdateFrom(const VirtualObject& other) {
bool changed = status_ != other.status_;
status_ = other.status_;
changed = replacement_ != other.replacement_ || changed;
replacement_ = other.replacement_;
if (fields_.size() != other.fields_.size()) {
fields_ = other.fields_;
return true;
}
for (size_t i = 0; i < fields_.size(); ++i) {
if (fields_[i] != other.fields_[i]) {
changed = true;
fields_[i] = other.fields_[i];
}
}
return changed;
}
VirtualState::VirtualState(Zone* zone, size_t size)
: info_(zone), last_changed_(nullptr) {
info_.resize(size);
}
VirtualState::VirtualState(const VirtualState& state)
: info_(state.info_.get_allocator().zone()),
last_changed_(state.last_changed_) {
info_.resize(state.info_.size());
for (size_t i = 0; i < state.info_.size(); ++i) {
if (state.info_[i] && state.info_[i]->id() == i) {
info_[i] = new (state.info_.get_allocator().zone())
VirtualObject(*state.info_[i]);
}
}
for (size_t i = 0; i < state.info_.size(); ++i) {
if (state.info_[i] && state.info_[i]->id() != i) {
info_[i] = info_[state.info_[i]->id()];
}
}
}
VirtualObject* VirtualState::GetVirtualObject(size_t id) { return info_[id]; }
VirtualObject* VirtualState::GetVirtualObject(Node* node) {
return GetVirtualObject(node->id());
}
VirtualObject* VirtualState::GetOrCreateTrackedVirtualObject(NodeId id,
Zone* zone) {
if (VirtualObject* obj = GetVirtualObject(id)) {
return obj;
}
VirtualObject* obj = new (zone) VirtualObject(id, zone, 0);
SetVirtualObject(id, obj);
return obj;
}
void VirtualState::SetVirtualObject(NodeId id, VirtualObject* obj) {
info_[id] = obj;
}
bool VirtualState::UpdateFrom(NodeId id, VirtualObject* fromObj, Zone* zone) {
VirtualObject* obj = GetVirtualObject(id);
if (!obj) {
obj = new (zone) VirtualObject(*fromObj);
SetVirtualObject(id, obj);
if (FLAG_trace_turbo_escape) {
PrintF(" Taking field for #%d from %p\n", id,
static_cast<void*>(fromObj));
}
return true;
}
if (obj->UpdateFrom(*fromObj)) {
if (FLAG_trace_turbo_escape) {
PrintF(" Updating field for #%d from %p\n", id,
static_cast<void*>(fromObj));
}
return true;
}
return false;
}
bool VirtualState::UpdateFrom(VirtualState* from, Zone* zone) {
DCHECK_EQ(size(), from->size());
bool changed = false;
for (NodeId id = 0; id < size(); ++id) {
VirtualObject* ls = GetVirtualObject(id);
VirtualObject* rs = from->GetVirtualObject(id);
if (rs == nullptr) {
continue;
}
if (ls == nullptr) {
ls = new (zone) VirtualObject(*rs);
SetVirtualObject(id, ls);
changed = true;
continue;
}
if (FLAG_trace_turbo_escape) {
PrintF(" Updating fields of #%d\n", id);
}
changed = ls->UpdateFrom(*rs) || changed;
}
return false;
}
bool VirtualState::MergeFrom(VirtualState* left, VirtualState* right,
Zone* zone, Graph* graph,
CommonOperatorBuilder* common, Node* control) {
bool changed = false;
for (NodeId id = 0; id < std::min(left->size(), right->size()); ++id) {
VirtualObject* ls = left->GetVirtualObject(id);
VirtualObject* rs = right->GetVirtualObject(id);
if (ls != nullptr && rs != nullptr) {
if (FLAG_trace_turbo_escape) {
PrintF(" Merging fields of #%d\n", id);
}
VirtualObject* mergeObject = GetOrCreateTrackedVirtualObject(id, zone);
size_t fields = std::max(ls->fields(), rs->fields());
changed = mergeObject->ResizeFields(fields) || changed;
for (size_t i = 0; i < fields; ++i) {
if (ls->GetField(i) == rs->GetField(i)) {
changed = mergeObject->SetField(i, ls->GetField(i)) || changed;
if (FLAG_trace_turbo_escape && ls->GetField(i)) {
PrintF(" Field %zu agree on rep #%d\n", i,
ls->GetField(i)->id());
}
} else if (ls->GetField(i) != nullptr && rs->GetField(i) != nullptr) {
Node* phi = graph->NewNode(common->Phi(kMachAnyTagged, 2),
ls->GetField(i), rs->GetField(i), control);
if (mergeObject->SetField(i, phi)) {
if (FLAG_trace_turbo_escape) {
PrintF(" Creating Phi #%d as merge of #%d and #%d\n",
phi->id(), ls->GetField(i)->id(), rs->GetField(i)->id());
}
changed = true;
}
} else {
changed = mergeObject->SetField(i, nullptr) || changed;
}
}
}
}
return changed;
}
Node* VirtualState::ResolveReplacement(Node* node) {
if (VirtualObject* obj = GetVirtualObject(node)) {
if (Node* rep = obj->GetReplacement()) {
return rep;
}
}
return node;
}
bool VirtualState::UpdateReplacement(Node* node, Node* rep, Zone* zone) {
if (!GetVirtualObject(node)) {
SetVirtualObject(node->id(), new (zone) VirtualObject(node->id(), zone));
}
if (GetVirtualObject(node)->SetReplacement(rep)) {
LastChangedAt(node);
if (FLAG_trace_turbo_escape) {
PrintF("Representation of #%d is #%d (%s)\n", node->id(), rep->id(),
rep->op()->mnemonic());
}
return true;
}
return false;
}
// ------------------------------EscapeStatusAnalysis---------------------------
EscapeStatusAnalysis::EscapeStatusAnalysis(
EscapeObjectAnalysis* object_analysis, Graph* graph, Zone* zone)
: object_analysis_(object_analysis),
graph_(graph),
zone_(zone),
info_(zone),
queue_(zone) {}
EscapeStatusAnalysis::~EscapeStatusAnalysis() {}
bool EscapeStatusAnalysis::HasEntry(Node* node) {
return info_[node->id()] != kUnknown;
}
bool EscapeStatusAnalysis::IsVirtual(Node* node) {
if (node->id() >= info_.size()) {
return false;
}
return info_[node->id()] == kVirtual;
}
bool EscapeStatusAnalysis::IsEscaped(Node* node) {
return info_[node->id()] == kEscaped;
}
bool EscapeStatusAnalysis::SetEscaped(Node* node) {
bool changed = info_[node->id()] != kEscaped;
info_[node->id()] = kEscaped;
return changed;
}
void EscapeStatusAnalysis::Run() {
info_.resize(graph()->NodeCount());
ZoneVector<bool> visited(zone());
visited.resize(graph()->NodeCount());
queue_.push_back(graph()->end());
while (!queue_.empty()) {
Node* node = queue_.front();
queue_.pop_front();
Process(node);
if (!visited[node->id()]) {
RevisitInputs(node);
}
visited[node->id()] = true;
}
if (FLAG_trace_turbo_escape) {
DebugPrint();
}
}
void EscapeStatusAnalysis::RevisitInputs(Node* node) {
for (Edge edge : node->input_edges()) {
Node* input = edge.to();
queue_.push_back(input);
}
}
void EscapeStatusAnalysis::RevisitUses(Node* node) {
for (Edge edge : node->use_edges()) {
Node* use = edge.from();
queue_.push_back(use);
}
}
void EscapeStatusAnalysis::Process(Node* node) {
switch (node->opcode()) {
case IrOpcode::kAllocate:
ProcessAllocate(node);
break;
case IrOpcode::kFinishRegion:
ProcessFinishRegion(node);
break;
case IrOpcode::kStoreField:
ProcessStoreField(node);
break;
case IrOpcode::kLoadField: {
if (Node* rep = object_analysis_->GetReplacement(node, node->id())) {
if (rep->opcode() == IrOpcode::kAllocate ||
rep->opcode() == IrOpcode::kFinishRegion) {
if (CheckUsesForEscape(node, rep)) {
RevisitInputs(rep);
RevisitUses(rep);
}
}
}
break;
}
case IrOpcode::kPhi:
if (!HasEntry(node)) {
info_[node->id()] = kVirtual;
}
CheckUsesForEscape(node);
default:
break;
}
}
void EscapeStatusAnalysis::ProcessStoreField(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kStoreField);
Node* to = NodeProperties::GetValueInput(node, 0);
Node* val = NodeProperties::GetValueInput(node, 1);
if (IsEscaped(to) && SetEscaped(val)) {
RevisitUses(val);
if (FLAG_trace_turbo_escape) {
PrintF("Setting #%d (%s) to escaped because of store to field of #%d\n",
val->id(), val->op()->mnemonic(), to->id());
}
}
}
void EscapeStatusAnalysis::ProcessAllocate(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kAllocate);
if (!HasEntry(node)) {
info_[node->id()] = kVirtual;
if (FLAG_trace_turbo_escape) {
PrintF("Created status entry for node #%d (%s)\n", node->id(),
node->op()->mnemonic());
}
NumberMatcher size(node->InputAt(0));
if (!size.HasValue() && SetEscaped(node)) {
RevisitUses(node);
if (FLAG_trace_turbo_escape) {
PrintF("Setting #%d to escaped because of non-const alloc\n",
node->id());
}
// This node is known to escape, uses do not have to be checked.
return;
}
}
if (CheckUsesForEscape(node)) {
RevisitUses(node);
}
}
bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep) {
for (Edge edge : uses->use_edges()) {
Node* use = edge.from();
if (!NodeProperties::IsValueEdge(edge)) continue;
switch (use->opcode()) {
case IrOpcode::kStoreField:
case IrOpcode::kLoadField:
case IrOpcode::kFrameState:
case IrOpcode::kStateValues:
case IrOpcode::kReferenceEqual:
case IrOpcode::kFinishRegion:
case IrOpcode::kPhi:
if (HasEntry(use) && IsEscaped(use) && SetEscaped(rep)) {
if (FLAG_trace_turbo_escape) {
PrintF(
"Setting #%d (%s) to escaped because of use by escaping node "
"#%d (%s)\n",
rep->id(), rep->op()->mnemonic(), use->id(),
use->op()->mnemonic());
}
return true;
}
break;
default:
if (SetEscaped(rep)) {
if (FLAG_trace_turbo_escape) {
PrintF("Setting #%d (%s) to escaped because of use by #%d (%s)\n",
rep->id(), rep->op()->mnemonic(), use->id(),
use->op()->mnemonic());
}
return true;
}
if (use->op()->EffectInputCount() == 0 &&
uses->op()->EffectInputCount() > 0 &&
uses->opcode() != IrOpcode::kLoadField) {
if (FLAG_trace_turbo_escape) {
PrintF("Encountered unaccounted use by #%d (%s)\n", use->id(),
use->op()->mnemonic());
}
UNREACHABLE();
}
}
}
return false;
}
void EscapeStatusAnalysis::ProcessFinishRegion(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion);
if (!HasEntry(node)) {
info_[node->id()] = kVirtual;
RevisitUses(node);
}
if (CheckUsesForEscape(node)) {
RevisitInputs(node);
}
}
void EscapeStatusAnalysis::DebugPrint() {
for (NodeId id = 0; id < info_.size(); id++) {
if (info_[id] != kUnknown) {
PrintF("Node #%d is %s\n", id,
info_[id] == kEscaped ? "escaping" : "virtual");
}
}
}
// -----------------------------EscapeObjectAnalysis---------------------------
EscapeObjectAnalysis::EscapeObjectAnalysis(Graph* graph,
CommonOperatorBuilder* common,
Zone* zone)
: graph_(graph), common_(common), zone_(zone), virtual_states_(zone) {}
EscapeObjectAnalysis::~EscapeObjectAnalysis() {}
void EscapeObjectAnalysis::Run() {
virtual_states_.resize(graph()->NodeCount());
ZoneVector<Node*> queue(zone());
queue.push_back(graph()->start());
while (!queue.empty()) {
Node* node = queue.back();
queue.pop_back();
if (Process(node)) {
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsEffectEdge(edge)) {
Node* use = edge.from();
if (use->opcode() != IrOpcode::kLoadField ||
!IsDanglingEffectNode(use)) {
queue.push_back(use);
}
}
}
// First process loads: dangling loads are a problem otherwise.
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsEffectEdge(edge)) {
Node* use = edge.from();
if (use->opcode() == IrOpcode::kLoadField &&
IsDanglingEffectNode(use)) {
queue.push_back(use);
}
}
}
}
}
if (FLAG_trace_turbo_escape) {
DebugPrint();
}
}
bool EscapeObjectAnalysis::IsDanglingEffectNode(Node* node) {
if (node->op()->EffectInputCount() == 0) return false;
if (node->op()->EffectOutputCount() == 0) return false;
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsEffectEdge(edge)) {
return false;
}
}
return true;
}
bool EscapeObjectAnalysis::Process(Node* node) {
switch (node->opcode()) {
case IrOpcode::kAllocate:
ProcessAllocation(node);
break;
case IrOpcode::kBeginRegion:
ForwardVirtualState(node);
break;
case IrOpcode::kFinishRegion:
ProcessFinishRegion(node);
break;
case IrOpcode::kStoreField:
ProcessStoreField(node);
break;
case IrOpcode::kLoadField:
ProcessLoadField(node);
break;
case IrOpcode::kStart:
ProcessStart(node);
break;
case IrOpcode::kEffectPhi:
return ProcessEffectPhi(node);
break;
default:
if (node->op()->EffectInputCount() > 0) {
ForwardVirtualState(node);
}
break;
}
return true;
}
bool EscapeObjectAnalysis::IsEffectBranchPoint(Node* node) {
int count = 0;
for (Edge edge : node->use_edges()) {
Node* use = edge.from();
if (NodeProperties::IsEffectEdge(edge) &&
use->opcode() != IrOpcode::kLoadField) {
if (++count > 1) {
return true;
}
}
}
return false;
}
void EscapeObjectAnalysis::ForwardVirtualState(Node* node) {
DCHECK_EQ(node->op()->EffectInputCount(), 1);
if (node->opcode() != IrOpcode::kLoadField && IsDanglingEffectNode(node)) {
PrintF("Dangeling effect node: #%d (%s)\n", node->id(),
node->op()->mnemonic());
DCHECK(false);
}
Node* effect = NodeProperties::GetEffectInput(node);
// Break the cycle for effect phis.
if (effect->opcode() == IrOpcode::kEffectPhi) {
if (virtual_states_[effect->id()] == nullptr) {
virtual_states_[effect->id()] =
new (zone()) VirtualState(zone(), graph()->NodeCount());
}
}
DCHECK_NOT_NULL(virtual_states_[effect->id()]);
if (IsEffectBranchPoint(effect)) {
if (virtual_states_[node->id()]) return;
virtual_states_[node->id()] =
new (zone()) VirtualState(*virtual_states_[effect->id()]);
if (FLAG_trace_turbo_escape) {
PrintF("Copying object state %p from #%d (%s) to #%d (%s)\n",
static_cast<void*>(virtual_states_[effect->id()]), effect->id(),
effect->op()->mnemonic(), node->id(), node->op()->mnemonic());
}
} else {
virtual_states_[node->id()] = virtual_states_[effect->id()];
if (FLAG_trace_turbo_escape) {
PrintF("Forwarding object state %p from #%d (%s) to #%d (%s)\n",
static_cast<void*>(virtual_states_[effect->id()]), effect->id(),
effect->op()->mnemonic(), node->id(), node->op()->mnemonic());
}
}
}
void EscapeObjectAnalysis::ProcessStart(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kStart);
virtual_states_[node->id()] =
new (zone()) VirtualState(zone(), graph()->NodeCount());
}
bool EscapeObjectAnalysis::ProcessEffectPhi(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kEffectPhi);
// For now only support binary phis.
DCHECK_EQ(node->op()->EffectInputCount(), 2);
Node* left = NodeProperties::GetEffectInput(node, 0);
Node* right = NodeProperties::GetEffectInput(node, 1);
bool changed = false;
VirtualState* mergeState = virtual_states_[node->id()];
if (!mergeState) {
mergeState = new (zone()) VirtualState(zone(), graph()->NodeCount());
virtual_states_[node->id()] = mergeState;
changed = true;
if (FLAG_trace_turbo_escape) {
PrintF("Phi #%d got new states map %p.\n", node->id(),
static_cast<void*>(mergeState));
}
} else if (mergeState->GetLastChanged() != node) {
changed = true;
}
VirtualState* l = virtual_states_[left->id()];
VirtualState* r = virtual_states_[right->id()];
if (l == nullptr && r == nullptr) {
return changed;
}
if (FLAG_trace_turbo_escape) {
PrintF("At Phi #%d, merging states %p (from #%d) and %p (from #%d)\n",
node->id(), static_cast<void*>(l), left->id(), static_cast<void*>(r),
right->id());
}
if (r && l == nullptr) {
changed = mergeState->UpdateFrom(r, zone()) || changed;
} else if (l && r == nullptr) {
changed = mergeState->UpdateFrom(l, zone()) || changed;
} else {
changed = mergeState->MergeFrom(l, r, zone(), graph(), common(),
NodeProperties::GetControlInput(node)) ||
changed;
}
if (FLAG_trace_turbo_escape) {
PrintF("Merge %s the node.\n", changed ? "changed" : "did not change");
}
if (changed) {
mergeState->LastChangedAt(node);
}
return changed;
}
void EscapeObjectAnalysis::ProcessAllocation(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kAllocate);
ForwardVirtualState(node);
// Check if we have already processed this node.
if (virtual_states_[node->id()]->GetVirtualObject(node)) return;
NumberMatcher size(node->InputAt(0));
if (size.HasValue()) {
virtual_states_[node->id()]->SetVirtualObject(
node->id(), new (zone()) VirtualObject(node->id(), zone(),
size.Value() / kPointerSize));
} else {
virtual_states_[node->id()]->SetVirtualObject(
node->id(), new (zone()) VirtualObject(node->id(), zone()));
}
virtual_states_[node->id()]->LastChangedAt(node);
}
void EscapeObjectAnalysis::ProcessFinishRegion(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion);
ForwardVirtualState(node);
Node* allocation = NodeProperties::GetValueInput(node, 0);
if (allocation->opcode() == IrOpcode::kAllocate) {
VirtualState* states = virtual_states_[node->id()];
DCHECK_NOT_NULL(states->GetVirtualObject(allocation));
if (!states->GetVirtualObject(node->id())) {
states->SetVirtualObject(node->id(),
states->GetVirtualObject(allocation));
if (FLAG_trace_turbo_escape) {
PrintF("Linked finish region node #%d to node #%d\n", node->id(),
allocation->id());
}
states->LastChangedAt(node);
}
}
}
Node* EscapeObjectAnalysis::GetReplacement(Node* at, NodeId id) {
VirtualState* states = virtual_states_[at->id()];
if (VirtualObject* obj = states->GetVirtualObject(id)) {
return obj->GetReplacement();
}
return nullptr;
}
int EscapeObjectAnalysis::OffsetFromAccess(Node* node) {
DCHECK(OpParameter<FieldAccess>(node).offset % kPointerSize == 0);
return OpParameter<FieldAccess>(node).offset / kPointerSize;
}
void EscapeObjectAnalysis::ProcessLoadField(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kLoadField);
ForwardVirtualState(node);
Node* from = NodeProperties::GetValueInput(node, 0);
int offset = OffsetFromAccess(node);
VirtualState* states = virtual_states_[node->id()];
if (VirtualObject* state = states->GetVirtualObject(from)) {
if (!state->IsTracked()) return;
Node* value = state->GetField(offset);
if (value) {
// Record that the load has this alias.
states->UpdateReplacement(node, value, zone());
} else if (FLAG_trace_turbo_escape) {
PrintF("No field %d on record for #%d\n", offset, from->id());
}
} else {
if (from->opcode() == IrOpcode::kPhi) {
// Only binary phis are supported for now.
CHECK_EQ(from->op()->ValueInputCount(), 2);
if (FLAG_trace_turbo_escape) {
PrintF("Load #%d from phi #%d", node->id(), from->id());
}
Node* left = NodeProperties::GetValueInput(from, 0);
Node* right = NodeProperties::GetValueInput(from, 1);
VirtualObject* l = states->GetVirtualObject(left);
VirtualObject* r = states->GetVirtualObject(right);
if (l && r) {
Node* lv = l->GetField(offset);
Node* rv = r->GetField(offset);
if (lv && rv) {
if (!states->GetVirtualObject(node)) {
states->SetVirtualObject(
node->id(), new (zone()) VirtualObject(node->id(), zone()));
}
Node* rep = states->GetVirtualObject(node)->GetReplacement();
if (!rep || rep->opcode() != IrOpcode::kPhi ||
NodeProperties::GetValueInput(rep, 0) != lv ||
NodeProperties::GetValueInput(rep, 1) != rv) {
Node* phi =
graph()->NewNode(common()->Phi(kMachAnyTagged, 2), lv, rv,
NodeProperties::GetControlInput(from));
states->GetVirtualObject(node)->SetReplacement(phi);
states->LastChangedAt(node);
if (FLAG_trace_turbo_escape) {
PrintF(" got phi of #%d is #%d created.\n", lv->id(), rv->id());
}
} else if (FLAG_trace_turbo_escape) {
PrintF(" has already the right phi representation.\n");
}
} else if (FLAG_trace_turbo_escape) {
PrintF(" has incomplete field info: %p %p\n", static_cast<void*>(lv),
static_cast<void*>(rv));
}
} else if (FLAG_trace_turbo_escape) {
PrintF(" has incomplete virtual object info: %p %p\n",
static_cast<void*>(l), static_cast<void*>(r));
}
}
}
}
void EscapeObjectAnalysis::ProcessStoreField(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kStoreField);
ForwardVirtualState(node);
Node* to = NodeProperties::GetValueInput(node, 0);
Node* val = NodeProperties::GetValueInput(node, 1);
int offset = OffsetFromAccess(node);
VirtualState* states = virtual_states_[node->id()];
if (VirtualObject* obj = states->GetVirtualObject(to)) {
if (!obj->IsTracked()) return;
if (obj->SetField(offset, states->ResolveReplacement(val))) {
states->LastChangedAt(node);
}
}
}
void EscapeObjectAnalysis::DebugPrint() {
ZoneVector<VirtualState*> object_states(zone());
for (NodeId id = 0; id < virtual_states_.size(); id++) {
if (VirtualState* states = virtual_states_[id]) {
if (std::find(object_states.begin(), object_states.end(), states) ==
object_states.end()) {
object_states.push_back(states);
}
}
}
for (size_t n = 0; n < object_states.size(); n++) {
PrintF("Dumping object state %p\n", static_cast<void*>(object_states[n]));
for (size_t id = 0; id < object_states[n]->size(); id++) {
if (VirtualObject* obj = object_states[n]->GetVirtualObject(id)) {
if (obj->id() == id) {
PrintF(" Object #%zu with %zu fields", id, obj->fields());
if (Node* rep = obj->GetReplacement()) {
PrintF(", rep = #%d (%s)", rep->id(), rep->op()->mnemonic());
}
PrintF("\n");
for (size_t i = 0; i < obj->fields(); ++i) {
if (Node* f = obj->GetField(i)) {
PrintF(" Field %zu = #%d (%s)\n", i, f->id(),
f->op()->mnemonic());
}
}
} else {
PrintF(" Object #%zu links to object #%d\n", id, obj->id());
}
}
}
}
}
} // namespace compiler
} // namespace internal
} // namespace v8
// Copyright 2015 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.
#ifndef V8_COMPILER_ESCAPE_ANALYSIS_H_
#define V8_COMPILER_ESCAPE_ANALYSIS_H_
#include "src/base/flags.h"
#include "src/compiler/graph.h"
namespace v8 {
namespace internal {
namespace compiler {
// Forward declarations.
class CommonOperatorBuilder;
class EscapeObjectAnalysis;
class VirtualState;
// EscapeStatusAnalysis determines for each allocation whether it escapes.
class EscapeStatusAnalysis {
public:
EscapeStatusAnalysis(EscapeObjectAnalysis* object_analysis, Graph* graph,
Zone* zone);
~EscapeStatusAnalysis();
enum EscapeStatusFlag {
kUnknown = 0u,
kVirtual = 1u << 0,
kEscaped = 1u << 1,
};
typedef base::Flags<EscapeStatusFlag> EscapeStatusFlags;
void Run();
bool HasEntry(Node* node);
bool IsVirtual(Node* node);
bool IsEscaped(Node* node);
void DebugPrint();
private:
void Process(Node* node);
void ProcessAllocate(Node* node);
void ProcessFinishRegion(Node* node);
void ProcessStoreField(Node* node);
bool CheckUsesForEscape(Node* node) { return CheckUsesForEscape(node, node); }
bool CheckUsesForEscape(Node* node, Node* rep);
void RevisitUses(Node* node);
void RevisitInputs(Node* node);
bool SetEscaped(Node* node);
Graph* graph() const { return graph_; }
Zone* zone() const { return zone_; }
EscapeObjectAnalysis* object_analysis_;
Graph* const graph_;
Zone* const zone_;
ZoneVector<EscapeStatusFlags> info_;
ZoneDeque<Node*> queue_;
DISALLOW_COPY_AND_ASSIGN(EscapeStatusAnalysis);
};
DEFINE_OPERATORS_FOR_FLAGS(EscapeStatusAnalysis::EscapeStatusFlags)
// EscapeObjectAnalysis simulates stores to determine values of loads if
// an object is virtual and eliminated.
class EscapeObjectAnalysis {
public:
EscapeObjectAnalysis(Graph* graph, CommonOperatorBuilder* common, Zone* zone);
~EscapeObjectAnalysis();
void Run();
Node* GetReplacement(Node* at, NodeId id);
private:
bool Process(Node* node);
void ProcessLoadField(Node* node);
void ProcessStoreField(Node* node);
void ProcessAllocation(Node* node);
void ProcessFinishRegion(Node* node);
void ProcessCall(Node* node);
void ProcessStart(Node* node);
bool ProcessEffectPhi(Node* node);
void ForwardVirtualState(Node* node);
bool IsEffectBranchPoint(Node* node);
bool IsDanglingEffectNode(Node* node);
int OffsetFromAccess(Node* node);
void DebugPrint();
Graph* graph() const { return graph_; }
CommonOperatorBuilder* common() const { return common_; }
Zone* zone() const { return zone_; }
Graph* const graph_;
CommonOperatorBuilder* const common_;
Zone* const zone_;
ZoneVector<VirtualState*> virtual_states_;
DISALLOW_COPY_AND_ASSIGN(EscapeObjectAnalysis);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_ESCAPE_ANALYSIS_H_
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include "src/compiler/common-operator-reducer.h" #include "src/compiler/common-operator-reducer.h"
#include "src/compiler/control-flow-optimizer.h" #include "src/compiler/control-flow-optimizer.h"
#include "src/compiler/dead-code-elimination.h" #include "src/compiler/dead-code-elimination.h"
#include "src/compiler/escape-analysis.h"
#include "src/compiler/escape-analysis-reducer.h"
#include "src/compiler/frame-elider.h" #include "src/compiler/frame-elider.h"
#include "src/compiler/graph-replay.h" #include "src/compiler/graph-replay.h"
#include "src/compiler/graph-trimmer.h" #include "src/compiler/graph-trimmer.h"
...@@ -646,6 +648,26 @@ struct BranchEliminationPhase { ...@@ -646,6 +648,26 @@ struct BranchEliminationPhase {
}; };
struct EscapeAnalysisPhase {
static const char* phase_name() { return "escape analysis"; }
void Run(PipelineData* data, Zone* temp_zone) {
EscapeObjectAnalysis escape_analysis(data->graph(),
data->jsgraph()->common(), temp_zone);
escape_analysis.Run();
EscapeStatusAnalysis escape_status(&escape_analysis, data->graph(),
temp_zone);
escape_status.Run();
JSGraphReducer graph_reducer(data->jsgraph(), temp_zone);
EscapeAnalysisReducer escape_reducer(&graph_reducer, data->jsgraph(),
&escape_status, &escape_analysis,
temp_zone);
AddReducer(data, &graph_reducer, &escape_reducer);
graph_reducer.ReduceGraph();
}
};
struct SimplifiedLoweringPhase { struct SimplifiedLoweringPhase {
static const char* phase_name() { return "simplified lowering"; } static const char* phase_name() { return "simplified lowering"; }
...@@ -1148,6 +1170,11 @@ Handle<Code> Pipeline::GenerateCode() { ...@@ -1148,6 +1170,11 @@ Handle<Code> Pipeline::GenerateCode() {
RunPrintAndVerify("Loop peeled"); RunPrintAndVerify("Loop peeled");
} }
if (FLAG_turbo_escape) {
Run<EscapeAnalysisPhase>();
RunPrintAndVerify("Escape Analysed");
}
// Lower simplified operators and insert changes. // Lower simplified operators and insert changes.
Run<SimplifiedLoweringPhase>(); Run<SimplifiedLoweringPhase>();
RunPrintAndVerify("Lowered simplified"); RunPrintAndVerify("Lowered simplified");
......
...@@ -463,6 +463,8 @@ DEFINE_BOOL(turbo_cf_optimization, true, "optimize control flow in TurboFan") ...@@ -463,6 +463,8 @@ DEFINE_BOOL(turbo_cf_optimization, true, "optimize control flow in TurboFan")
DEFINE_BOOL(turbo_frame_elision, true, "elide frames in TurboFan") DEFINE_BOOL(turbo_frame_elision, true, "elide frames in TurboFan")
DEFINE_BOOL(turbo_cache_shared_code, true, "cache context-independent code") DEFINE_BOOL(turbo_cache_shared_code, true, "cache context-independent code")
DEFINE_BOOL(turbo_preserve_shared_code, false, "keep context-independent code") DEFINE_BOOL(turbo_preserve_shared_code, false, "keep context-independent code")
DEFINE_BOOL(turbo_escape, false, "enable escape analysis")
DEFINE_BOOL(trace_turbo_escape, false, "enable tracing in escape analysis")
#if defined(V8_WASM) #if defined(V8_WASM)
// Flags for native WebAssembly. // Flags for native WebAssembly.
......
// Copyright 2015 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 --turbo-escape
//
function f(a) {
"use strict";
return arguments.length;
}
function g() {
return f(1,2,3);
}
assertEquals(3, g());
assertEquals(3, g());
%OptimizeFunctionOnNextCall(g);
assertEquals(3, g());
// Copyright 2015 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 --turbo-escape
//
function f(a) {
"use strict";
if (arguments === a)
return 1;
return arguments.length;
}
function g(a) {
return f(a,1,2,3);
}
assertEquals(4, g());
assertEquals(4, g());
%OptimizeFunctionOnNextCall(g);
assertEquals(4, g());
// Copyright 2015 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 --turbo-escape
//
function f(a) {
"use strict";
return arguments.length;
}
function g() {
"use strict";
return arguments[f(1,2)];
}
assertEquals(6, g(4,5,6));
assertEquals(6, g(4,5,6));
%OptimizeFunctionOnNextCall(g);
assertEquals(6, g(4,5,6));
// Copyright 2015 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 --turbo-escape
//
function f(a) {
"use strict";
return arguments.length;
}
function h() {
"use strict";
return arguments;
}
function g() {
return "" + f(1,2,3) + " " + h(4,5,6);
}
assertEquals("3 [object Arguments]", g());
assertEquals("3 [object Arguments]", g());
%OptimizeFunctionOnNextCall(g);
assertEquals("3 [object Arguments]", g());
// Copyright 2015 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 --turbo-escape
//
function f(h) {
"use strict";
h(arguments);
return arguments.length;
}
function g(h) {
return f(h,1,2,3);
}
function h(x) {
assertEquals("[object Arguments]", ""+x)
}
assertEquals(4, g(h));
assertEquals(4, g(h));
%OptimizeFunctionOnNextCall(g);
assertEquals(4, g(h));
// Copyright 2015 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 --turbo-escape
//
function f(a) {
"use strict";
return arguments;
}
function g() {
"use strict";
var x = f(1,2,3);
while (x.length < 4) {
x = f(4,5,6,7,8);
}
return x.length;
}
assertEquals(5, g());
assertEquals(5, g());
%OptimizeFunctionOnNextCall(g);
assertEquals(5, g());
// Copyright 2015 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 --turbo-escape
//
function f(a) {
"use strict";
return arguments;
}
function g(a) {
"use strict";
var x = f(1,2,3);
if (a) {
x[1] = 5;
} else {
x[1] = 7;
}
return x[1];
}
assertEquals(7, g());
assertEquals(7, g());
%OptimizeFunctionOnNextCall(g);
assertEquals(7, g());
// Copyright 2015 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 --turbo-escape
//
function f(a) {
this.x=a;
this.y=1;
}
function g() {
"use strict";
var o = new f(2);
while (o.y < 4) {
o.x = 5;
o.y = 5;
}
return o.x;
}
assertEquals(5, g());
assertEquals(5, g());
%OptimizeFunctionOnNextCall(g);
assertEquals(5, g());
...@@ -167,6 +167,18 @@ ...@@ -167,6 +167,18 @@
# TODO(titzer): too slow in --turbo mode due to O(n^2) graph verification. # TODO(titzer): too slow in --turbo mode due to O(n^2) graph verification.
'regress/regress-1122': [PASS, NO_VARIANTS], 'regress/regress-1122': [PASS, NO_VARIANTS],
# TODO(sigurds): Tests that may fail because of missing deopt support.
'compiler/escape-analysis-1': [PASS, NO_VARIANTS],
'compiler/escape-analysis-2': [PASS, NO_VARIANTS],
'compiler/escape-analysis-3': [PASS, NO_VARIANTS],
'compiler/escape-analysis-4': [PASS, NO_VARIANTS],
'compiler/escape-analysis-5': [PASS, NO_VARIANTS],
'compiler/escape-analysis-7': [PASS, NO_VARIANTS],
# TODO(sigurds): Tests that fail because of incomplete phi handling.
'compiler/escape-analysis-6': [PASS, NO_VARIANTS],
# TODO(sigurds): Tests that fail because of incomplete use handling (i.e. select).
'compiler/escape-analysis-8': [PASS, NO_VARIANTS],
# Assumptions about optimization need investigation in TurboFan. # Assumptions about optimization need investigation in TurboFan.
'regress-sync-optimized-lists': [PASS, NO_VARIANTS], 'regress-sync-optimized-lists': [PASS, NO_VARIANTS],
......
// Copyright 2015 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.
#include "src/bit-vector.h"
#include "src/compiler/escape-analysis.h"
#include "src/compiler/escape-analysis-reducer.h"
#include "src/compiler/graph-visualizer.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/simplified-operator.h"
#include "src/types-inl.h"
#include "src/zone-containers.h"
#include "test/unittests/compiler/graph-unittest.h"
namespace v8 {
namespace internal {
namespace compiler {
class EscapeAnalysisTest : public GraphTest {
public:
EscapeAnalysisTest()
: simplified_(zone()),
jsgraph_(isolate(), graph(), common(), nullptr, nullptr, nullptr),
escape_objects_(graph(), common(), zone()),
escape_status_(&escape_objects_, graph(), zone()),
effect_(graph()->start()),
control_(graph()->start()) {}
~EscapeAnalysisTest() {}
EscapeStatusAnalysis* escape_status() { return &escape_status_; }
EscapeObjectAnalysis* escape_objects() { return &escape_objects_; }
protected:
void Analysis() {
escape_objects_.Run();
escape_status_.Run();
}
void Transformation() {
GraphReducer graph_reducer(zone(), graph());
EscapeAnalysisReducer escape_reducer(
&graph_reducer, &jsgraph_, &escape_status_, &escape_objects_, zone());
graph_reducer.AddReducer(&escape_reducer);
graph_reducer.ReduceGraph();
}
// ---------------------------------Node Creation Helper----------------------
Node* BeginRegion(Node* effect = nullptr) {
if (!effect) {
effect = effect_;
}
return effect_ = graph()->NewNode(common()->BeginRegion(), effect);
}
Node* FinishRegion(Node* value, Node* effect = nullptr) {
if (!effect) {
effect = effect_;
}
return effect_ = graph()->NewNode(common()->FinishRegion(), value, effect);
}
Node* Allocate(Node* size, Node* effect = nullptr, Node* control = nullptr) {
if (!effect) {
effect = effect_;
}
if (!control) {
control = control_;
}
return effect_ = graph()->NewNode(simplified()->Allocate(), size, effect,
control);
}
Node* Constant(int num) {
return graph()->NewNode(common()->NumberConstant(num));
}
Node* Store(const FieldAccess& access, Node* allocation, Node* value,
Node* effect = nullptr, Node* control = nullptr) {
if (!effect) {
effect = effect_;
}
if (!control) {
control = control_;
}
return effect_ = graph()->NewNode(simplified()->StoreField(access),
allocation, value, effect, control);
}
Node* Load(const FieldAccess& access, Node* from, Node* effect = nullptr,
Node* control = nullptr) {
if (!effect) {
effect = effect_;
}
if (!control) {
control = control_;
}
return graph()->NewNode(simplified()->LoadField(access), from, effect,
control);
}
Node* Return(Node* value, Node* effect = nullptr, Node* control = nullptr) {
if (!effect) {
effect = effect_;
}
if (!control) {
control = control_;
}
return control_ =
graph()->NewNode(common()->Return(), value, effect, control);
}
void EndGraph() {
for (Edge edge : graph()->end()->input_edges()) {
if (NodeProperties::IsControlEdge(edge)) {
edge.UpdateTo(control_);
}
}
}
Node* Branch() {
return control_ =
graph()->NewNode(common()->Branch(), Constant(0), control_);
}
Node* IfTrue() {
return control_ = graph()->NewNode(common()->IfTrue(), control_);
}
Node* IfFalse() { return graph()->NewNode(common()->IfFalse(), control_); }
Node* Merge2(Node* control1, Node* control2) {
return control_ = graph()->NewNode(common()->Merge(2), control1, control2);
}
FieldAccess AccessAtIndex(int offset) {
FieldAccess access = {kTaggedBase, offset, MaybeHandle<Name>(), Type::Any(),
kMachAnyTagged};
return access;
}
// ---------------------------------Assertion Helper--------------------------
void ExpectReplacement(Node* node, Node* rep) {
EXPECT_EQ(rep, escape_objects()->GetReplacement(node, node->id()));
}
void ExpectReplacementPhi(Node* node, Node* left, Node* right) {
Node* rep = escape_objects()->GetReplacement(node, node->id());
ASSERT_NE(nullptr, rep);
ASSERT_EQ(IrOpcode::kPhi, rep->opcode());
EXPECT_EQ(left, NodeProperties::GetValueInput(rep, 0));
EXPECT_EQ(right, NodeProperties::GetValueInput(rep, 1));
}
void ExpectVirtual(Node* node) {
EXPECT_TRUE(node->opcode() == IrOpcode::kAllocate ||
node->opcode() == IrOpcode::kFinishRegion);
EXPECT_TRUE(escape_status()->IsVirtual(node));
}
void ExpectEscaped(Node* node) {
EXPECT_TRUE(node->opcode() == IrOpcode::kAllocate ||
node->opcode() == IrOpcode::kFinishRegion);
EXPECT_TRUE(escape_status()->IsEscaped(node));
}
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
Node* effect() { return effect_; }
private:
SimplifiedOperatorBuilder simplified_;
JSGraph jsgraph_;
EscapeObjectAnalysis escape_objects_;
EscapeStatusAnalysis escape_status_;
Node* effect_;
Node* control_;
};
// -----------------------------------------------------------------------------
// Test cases.
TEST_F(EscapeAnalysisTest, StraightNonEscape) {
Node* object1 = Constant(1);
BeginRegion();
Node* allocation = Allocate(Constant(kPointerSize));
Store(AccessAtIndex(0), allocation, object1);
Node* finish = FinishRegion(allocation);
Node* load = Load(AccessAtIndex(0), finish);
Node* result = Return(load);
EndGraph();
Analysis();
ExpectVirtual(allocation);
ExpectReplacement(load, object1);
Transformation();
ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0));
}
TEST_F(EscapeAnalysisTest, StraightEscape) {
Node* object1 = Constant(1);
BeginRegion();
Node* allocation = Allocate(Constant(kPointerSize));
Store(AccessAtIndex(0), allocation, object1);
Node* finish = FinishRegion(allocation);
Node* load = Load(AccessAtIndex(0), finish);
Node* result = Return(allocation);
EndGraph();
Analysis();
ExpectEscaped(allocation);
ExpectReplacement(load, object1);
Transformation();
ASSERT_EQ(allocation, NodeProperties::GetValueInput(result, 0));
}
TEST_F(EscapeAnalysisTest, StoreLoadEscape) {
Node* object1 = Constant(1);
BeginRegion();
Node* allocation1 = Allocate(Constant(kPointerSize));
Store(AccessAtIndex(0), allocation1, object1);
Node* finish1 = FinishRegion(allocation1);
BeginRegion();
Node* allocation2 = Allocate(Constant(kPointerSize));
Store(AccessAtIndex(0), allocation2, finish1);
Node* finish2 = FinishRegion(allocation2);
Node* load = Load(AccessAtIndex(0), finish2);
Node* result = Return(load);
EndGraph();
Analysis();
ExpectEscaped(allocation1);
ExpectVirtual(allocation2);
ExpectReplacement(load, finish1);
Transformation();
ASSERT_EQ(finish1, NodeProperties::GetValueInput(result, 0));
}
TEST_F(EscapeAnalysisTest, BranchNonEscape) {
Node* object1 = Constant(1);
Node* object2 = Constant(2);
BeginRegion();
Node* allocation = Allocate(Constant(kPointerSize));
Store(AccessAtIndex(0), allocation, object1);
Node* finish = FinishRegion(allocation);
Branch();
Node* ifFalse = IfFalse();
Node* ifTrue = IfTrue();
Node* effect1 = Store(AccessAtIndex(0), allocation, object1, finish, ifFalse);
Node* effect2 = Store(AccessAtIndex(0), allocation, object2, finish, ifTrue);
Node* merge = Merge2(ifFalse, ifTrue);
Node* phi = graph()->NewNode(common()->EffectPhi(2), effect1, effect2, merge);
Node* load = Load(AccessAtIndex(0), finish, phi, merge);
Node* result = Return(load, phi);
EndGraph();
Analysis();
ExpectVirtual(allocation);
ExpectReplacementPhi(load, object1, object2);
Node* replacement_phi = escape_objects()->GetReplacement(load, load->id());
Transformation();
ASSERT_EQ(replacement_phi, NodeProperties::GetValueInput(result, 0));
}
TEST_F(EscapeAnalysisTest, DanglingLoadOrder) {
Node* object1 = Constant(1);
Node* object2 = Constant(2);
Node* allocation = Allocate(Constant(kPointerSize));
Node* store1 = Store(AccessAtIndex(0), allocation, object1);
Node* load1 = Load(AccessAtIndex(0), allocation);
Store(AccessAtIndex(0), allocation, object2);
Node* load2 = Load(AccessAtIndex(0), allocation, store1);
Node* result = Return(load2);
EndGraph();
Analysis();
ExpectVirtual(allocation);
ExpectReplacement(load1, object1);
ExpectReplacement(load2, object1);
Transformation();
ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0));
}
} // namespace compiler
} // namespace internal
} // namespace v8
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
'compiler/control-flow-optimizer-unittest.cc', 'compiler/control-flow-optimizer-unittest.cc',
'compiler/dead-code-elimination-unittest.cc', 'compiler/dead-code-elimination-unittest.cc',
'compiler/diamond-unittest.cc', 'compiler/diamond-unittest.cc',
'compiler/escape-analysis-unittest.cc',
'compiler/graph-reducer-unittest.cc', 'compiler/graph-reducer-unittest.cc',
'compiler/graph-reducer-unittest.h', 'compiler/graph-reducer-unittest.h',
'compiler/graph-trimmer-unittest.cc', 'compiler/graph-trimmer-unittest.cc',
......
...@@ -498,6 +498,10 @@ ...@@ -498,6 +498,10 @@
'../../src/compiler/dead-code-elimination.cc', '../../src/compiler/dead-code-elimination.cc',
'../../src/compiler/dead-code-elimination.h', '../../src/compiler/dead-code-elimination.h',
'../../src/compiler/diamond.h', '../../src/compiler/diamond.h',
'../../src/compiler/escape-analysis.cc',
'../../src/compiler/escape-analysis.h',
"../../src/compiler/escape-analysis-reducer.cc",
"../../src/compiler/escape-analysis-reducer.h",
'../../src/compiler/frame.cc', '../../src/compiler/frame.cc',
'../../src/compiler/frame.h', '../../src/compiler/frame.h',
'../../src/compiler/frame-elider.cc', '../../src/compiler/frame-elider.cc',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment