Commit a2ad4c8f authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] New GraphReducer based LoadElimination.

Turn the LoadElimination into a proper graph Reducer so that it can run
together with ValueNumbering and RedundancyElimination to a fixpoint
for maximum load/check elimination. This also adds initial support for
eliminating redundant LoadElement/StoreElement nodes.

BUG=v8:4930,v8:5141

Review-Url: https://codereview.chromium.org/2164253002
Cr-Commit-Position: refs/heads/master@{#38015}
parent 480f155e
This diff is collapsed.
......@@ -5,30 +5,172 @@
#ifndef V8_COMPILER_LOAD_ELIMINATION_H_
#define V8_COMPILER_LOAD_ELIMINATION_H_
#include "src/compiler/graph-reducer.h"
namespace v8 {
namespace internal {
// Forward declarations.
class Zone;
namespace compiler {
// Forward declarations.
class Graph;
// Foward declarations.
struct FieldAccess;
// Eliminates redundant loads via scalar replacement of aggregates.
class LoadElimination final {
class LoadElimination final : public AdvancedReducer {
public:
LoadElimination(Graph* graph, Zone* zone) : graph_(graph), zone_(zone) {}
LoadElimination(Editor* editor, Zone* zone)
: AdvancedReducer(editor), node_states_(zone) {}
~LoadElimination() final {}
void Run();
Reduction Reduce(Node* node) final;
private:
Graph* graph() const { return graph_; }
Zone* zone() const { return zone_; }
static const size_t kMaxTrackedElements = 8;
// Abstract state to approximate the current state of an element along the
// effect paths through the graph.
class AbstractElements final : public ZoneObject {
public:
explicit AbstractElements(Zone* zone) {
for (size_t i = 0; i < arraysize(elements_); ++i) {
elements_[i] = Element();
}
}
AbstractElements(Node* object, Node* index, Node* value, Zone* zone)
: AbstractElements(zone) {
elements_[next_index_++] = Element(object, index, value);
}
AbstractElements const* Extend(Node* object, Node* index, Node* value,
Zone* zone) const {
AbstractElements* that = new (zone) AbstractElements(*this);
that->elements_[that->next_index_] = Element(object, index, value);
that->next_index_ = (that->next_index_ + 1) % arraysize(elements_);
return that;
}
Node* Lookup(Node* object, Node* index) const;
AbstractElements const* Kill(Node* object, Node* index, Zone* zone) const;
bool Equals(AbstractElements const* that) const;
AbstractElements const* Merge(AbstractElements const* that,
Zone* zone) const;
private:
struct Element {
Element() {}
Element(Node* object, Node* index, Node* value)
: object(object), index(index), value(value) {}
Node* object = nullptr;
Node* index = nullptr;
Node* value = nullptr;
};
Element elements_[kMaxTrackedElements];
size_t next_index_ = 0;
};
// Abstract state to approximate the current state of a certain field along
// the effect paths through the graph.
class AbstractField final : public ZoneObject {
public:
explicit AbstractField(Zone* zone) : info_for_node_(zone) {}
AbstractField(Node* object, Node* value, Zone* zone)
: info_for_node_(zone) {
info_for_node_.insert(std::make_pair(object, value));
}
AbstractField const* Extend(Node* object, Node* value, Zone* zone) const {
AbstractField* that = new (zone) AbstractField(zone);
that->info_for_node_ = this->info_for_node_;
that->info_for_node_.insert(std::make_pair(object, value));
return that;
}
Node* Lookup(Node* object) const;
AbstractField const* Kill(Node* object, Zone* zone) const;
bool Equals(AbstractField const* that) const {
return this == that || this->info_for_node_ == that->info_for_node_;
}
AbstractField const* Merge(AbstractField const* that, Zone* zone) const {
if (this->Equals(that)) return this;
AbstractField* copy = new (zone) AbstractField(zone);
for (auto this_it : this->info_for_node_) {
Node* this_object = this_it.first;
Node* this_value = this_it.second;
auto that_it = that->info_for_node_.find(this_object);
if (that_it != that->info_for_node_.end() &&
that_it->second == this_value) {
copy->info_for_node_.insert(this_it);
}
}
return copy;
}
private:
ZoneMap<Node*, Node*> info_for_node_;
};
static size_t const kMaxTrackedFields = 16;
class AbstractState final : public ZoneObject {
public:
AbstractState() {
for (size_t i = 0; i < arraysize(fields_); ++i) {
fields_[i] = nullptr;
}
}
bool Equals(AbstractState const* that) const;
void Merge(AbstractState const* that, Zone* zone);
AbstractState const* AddField(Node* object, size_t index, Node* value,
Zone* zone) const;
AbstractState const* KillField(Node* object, size_t index,
Zone* zone) const;
Node* LookupField(Node* object, size_t index) const;
AbstractState const* AddElement(Node* object, Node* index, Node* value,
Zone* zone) const;
AbstractState const* KillElement(Node* object, Node* index,
Zone* zone) const;
Node* LookupElement(Node* object, Node* index) const;
private:
AbstractElements const* elements_ = nullptr;
AbstractField const* fields_[kMaxTrackedFields];
};
class AbstractStateForEffectNodes final : public ZoneObject {
public:
explicit AbstractStateForEffectNodes(Zone* zone) : info_for_node_(zone) {}
AbstractState const* Get(Node* node) const;
void Set(Node* node, AbstractState const* state);
Zone* zone() const { return info_for_node_.get_allocator().zone(); }
private:
ZoneVector<AbstractState const*> info_for_node_;
};
Reduction ReduceLoadField(Node* node);
Reduction ReduceStoreField(Node* node);
Reduction ReduceLoadElement(Node* node);
Reduction ReduceStoreElement(Node* node);
Reduction ReduceEffectPhi(Node* node);
Reduction ReduceStart(Node* node);
Reduction ReduceOtherNode(Node* node);
Reduction UpdateState(Node* node, AbstractState const* state);
AbstractState const* ComputeLoopState(Node* node,
AbstractState const* state) const;
static int FieldIndexOf(FieldAccess const& access);
AbstractState const* empty_state() const { return &empty_state_; }
Zone* zone() const { return node_states_.zone(); }
AbstractState const empty_state_;
AbstractStateForEffectNodes node_states_;
Graph* const graph_;
Zone* const zone_;
DISALLOW_COPY_AND_ASSIGN(LoadElimination);
};
} // namespace compiler
......
......@@ -904,7 +904,6 @@ struct TypedLoweringPhase {
data->info()->is_deoptimization_enabled()
? JSIntrinsicLowering::kDeoptimizationEnabled
: JSIntrinsicLowering::kDeoptimizationDisabled);
ValueNumberingReducer value_numbering(temp_zone, data->graph()->zone());
SimplifiedOperatorReducer simple_reducer(&graph_reducer, data->jsgraph());
CheckpointElimination checkpoint_elimination(&graph_reducer);
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
......@@ -916,7 +915,6 @@ struct TypedLoweringPhase {
}
AddReducer(data, &graph_reducer, &typed_lowering);
AddReducer(data, &graph_reducer, &intrinsic_lowering);
AddReducer(data, &graph_reducer, &value_numbering);
AddReducer(data, &graph_reducer, &simple_reducer);
AddReducer(data, &graph_reducer, &checkpoint_elimination);
AddReducer(data, &graph_reducer, &common_reducer);
......@@ -1054,15 +1052,14 @@ struct LoadEliminationPhase {
static const char* phase_name() { return "load elimination"; }
void Run(PipelineData* data, Zone* temp_zone) {
// The memory optimizer requires the graphs to be trimmed, so trim now.
GraphTrimmer trimmer(temp_zone, data->graph());
NodeVector roots(temp_zone);
data->jsgraph()->GetCachedNodes(&roots);
trimmer.TrimGraph(roots.begin(), roots.end());
// Eliminate redundant loads.
LoadElimination load_elimination(data->graph(), temp_zone);
load_elimination.Run();
JSGraphReducer graph_reducer(data->jsgraph(), temp_zone);
RedundancyElimination redundancy_elimination(&graph_reducer, temp_zone);
LoadElimination load_elimination(&graph_reducer, temp_zone);
ValueNumberingReducer value_numbering(temp_zone, data->graph()->zone());
AddReducer(data, &graph_reducer, &redundancy_elimination);
AddReducer(data, &graph_reducer, &load_elimination);
AddReducer(data, &graph_reducer, &value_numbering);
graph_reducer.ReduceGraph();
}
};
......
......@@ -6,8 +6,13 @@
#include "src/compiler/access-builder.h"
#include "src/compiler/node.h"
#include "src/compiler/simplified-operator.h"
#include "test/unittests/compiler/graph-reducer-unittest.h"
#include "test/unittests/compiler/graph-unittest.h"
#include "test/unittests/compiler/node-test-utils.h"
#include "testing/gmock-support.h"
using testing::_;
using testing::StrictMock;
namespace v8 {
namespace internal {
......@@ -19,37 +24,122 @@ class LoadEliminationTest : public TypedGraphTest {
~LoadEliminationTest() override {}
protected:
void Run() {
LoadElimination load_elimination(graph(), zone());
load_elimination.Run();
}
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
private:
SimplifiedOperatorBuilder simplified_;
};
TEST_F(LoadEliminationTest, LoadElementAndLoadElement) {
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* control = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
ElementAccess const access = {kTaggedBase, kPointerSize, Type::Any(),
MachineType::AnyTagged(), kNoWriteBarrier};
StrictMock<MockAdvancedReducerEditor> editor;
LoadElimination load_elimination(&editor, zone());
load_elimination.Reduce(graph()->start());
Node* load1 = effect = graph()->NewNode(simplified()->LoadElement(access),
object, index, effect, control);
load_elimination.Reduce(load1);
Node* load2 = effect = graph()->NewNode(simplified()->LoadElement(access),
object, index, effect, control);
EXPECT_CALL(editor, ReplaceWithValue(load2, load1, load1, _));
Reduction r = load_elimination.Reduce(load2);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(load1, r.replacement());
}
TEST_F(LoadEliminationTest, StoreElementAndLoadElement) {
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* control = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
Node* value = Parameter(Type::Any(), 2);
ElementAccess const access = {kTaggedBase, kPointerSize, Type::Any(),
MachineType::AnyTagged(), kNoWriteBarrier};
StrictMock<MockAdvancedReducerEditor> editor;
LoadElimination load_elimination(&editor, zone());
load_elimination.Reduce(graph()->start());
Node* store = effect =
graph()->NewNode(simplified()->StoreElement(access), object, index, value,
effect, control);
load_elimination.Reduce(store);
Node* load = effect = graph()->NewNode(simplified()->LoadElement(access),
object, index, effect, control);
EXPECT_CALL(editor, ReplaceWithValue(load, value, store, _));
Reduction r = load_elimination.Reduce(load);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(value, r.replacement());
}
TEST_F(LoadEliminationTest, StoreElementAndStoreFieldAndLoadElement) {
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* control = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
Node* value = Parameter(Type::Any(), 2);
ElementAccess const access = {kTaggedBase, kPointerSize, Type::Any(),
MachineType::AnyTagged(), kNoWriteBarrier};
StrictMock<MockAdvancedReducerEditor> editor;
LoadElimination load_elimination(&editor, zone());
load_elimination.Reduce(graph()->start());
Node* store1 = effect =
graph()->NewNode(simplified()->StoreElement(access), object, index, value,
effect, control);
load_elimination.Reduce(store1);
Node* store2 = effect =
graph()->NewNode(simplified()->StoreField(AccessBuilder::ForMap()),
object, value, effect, control);
load_elimination.Reduce(store2);
Node* load = effect = graph()->NewNode(simplified()->LoadElement(access),
object, index, effect, control);
EXPECT_CALL(editor, ReplaceWithValue(load, value, store2, _));
Reduction r = load_elimination.Reduce(load);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(value, r.replacement());
}
TEST_F(LoadEliminationTest, LoadFieldAndLoadField) {
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* control = graph()->start();
FieldAccess access = {kTaggedBase,
kPointerSize,
MaybeHandle<Name>(),
Type::Any(),
MachineType::AnyTagged(),
kNoWriteBarrier};
FieldAccess const access = {kTaggedBase,
kPointerSize,
MaybeHandle<Name>(),
Type::Any(),
MachineType::AnyTagged(),
kNoWriteBarrier};
StrictMock<MockAdvancedReducerEditor> editor;
LoadElimination load_elimination(&editor, zone());
load_elimination.Reduce(graph()->start());
Node* load1 = effect = graph()->NewNode(simplified()->LoadField(access),
object, effect, control);
load_elimination.Reduce(load1);
Node* load2 = effect = graph()->NewNode(simplified()->LoadField(access),
object, effect, control);
control = graph()->NewNode(common()->Return(), load2, effect, control);
graph()->end()->ReplaceInput(0, control);
Run();
EXPECT_THAT(graph()->end(), IsEnd(IsReturn(load1, load1, graph()->start())));
EXPECT_CALL(editor, ReplaceWithValue(load2, load1, load1, _));
Reduction r = load_elimination.Reduce(load2);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(load1, r.replacement());
}
TEST_F(LoadEliminationTest, StoreFieldAndLoadField) {
......@@ -63,16 +153,57 @@ TEST_F(LoadEliminationTest, StoreFieldAndLoadField) {
Type::Any(),
MachineType::AnyTagged(),
kNoWriteBarrier};
StrictMock<MockAdvancedReducerEditor> editor;
LoadElimination load_elimination(&editor, zone());
load_elimination.Reduce(graph()->start());
Node* store = effect = graph()->NewNode(simplified()->StoreField(access),
object, value, effect, control);
load_elimination.Reduce(store);
Node* load = effect = graph()->NewNode(simplified()->LoadField(access),
object, effect, control);
control = graph()->NewNode(common()->Return(), load, effect, control);
graph()->end()->ReplaceInput(0, control);
EXPECT_CALL(editor, ReplaceWithValue(load, value, store, _));
Reduction r = load_elimination.Reduce(load);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(value, r.replacement());
}
Run();
TEST_F(LoadEliminationTest, StoreFieldAndStoreElementAndLoadField) {
Node* object = Parameter(Type::Any(), 0);
Node* value = Parameter(Type::Any(), 1);
Node* index = Parameter(Type::UnsignedSmall(), 2);
Node* effect = graph()->start();
Node* control = graph()->start();
FieldAccess access = {kTaggedBase,
kPointerSize,
MaybeHandle<Name>(),
Type::Any(),
MachineType::AnyTagged(),
kNoWriteBarrier};
StrictMock<MockAdvancedReducerEditor> editor;
LoadElimination load_elimination(&editor, zone());
load_elimination.Reduce(graph()->start());
Node* store1 = effect = graph()->NewNode(simplified()->StoreField(access),
object, value, effect, control);
load_elimination.Reduce(store1);
EXPECT_THAT(graph()->end(), IsEnd(IsReturn(value, store, graph()->start())));
Node* store2 = effect = graph()->NewNode(
simplified()->StoreElement(AccessBuilder::ForFixedArrayElement()), object,
index, object, effect, control);
load_elimination.Reduce(store2);
Node* load = effect = graph()->NewNode(simplified()->LoadField(access),
object, effect, control);
EXPECT_CALL(editor, ReplaceWithValue(load, value, store2, _));
Reduction r = load_elimination.Reduce(load);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(value, r.replacement());
}
} // namespace compiler
......
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