diff --git a/BUILD.gn b/BUILD.gn index e9f423c1ce..cbefe52086 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -3626,6 +3626,10 @@ v8_header_set("v8_internal_headers") { ] } + if (v8_enable_wasm_simd256_revec) { + sources += [ "src/compiler/linear-scheduler.h" ] + } + if (!v8_enable_third_party_heap) { sources += filter_include(v8_third_party_heap_files, [ "*.h" ]) } else { @@ -4071,6 +4075,10 @@ if (v8_enable_webassembly) { ] } +if (v8_enable_wasm_simd256_revec) { + v8_compiler_sources += [ "src/compiler/linear-scheduler.cc" ] +} + # The src/compiler files with optimizations. v8_source_set("v8_compiler_opt") { visibility = [ ":*" ] # Only targets in this file can depend on this. diff --git a/gni/v8.gni b/gni/v8.gni index 090e2b49cb..9cb853360f 100644 --- a/gni/v8.gni +++ b/gni/v8.gni @@ -69,6 +69,9 @@ declare_args() { # executed as standard JavaScript instead. v8_enable_webassembly = "" + # Enable 256-bit long vector re-vectorization pass in WASM compilation pipeline. + v8_enable_wasm_simd256_revec = false + # Enable runtime call stats. v8_enable_runtime_call_stats = !is_on_release_branch diff --git a/src/compiler/linear-scheduler.cc b/src/compiler/linear-scheduler.cc new file mode 100644 index 0000000000..c6fe65b482 --- /dev/null +++ b/src/compiler/linear-scheduler.cc @@ -0,0 +1,124 @@ +// Copyright 2013 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/linear-scheduler.h" + +#include "src/compiler/graph.h" +#include "src/compiler/node-properties.h" +#include "src/compiler/node.h" +#include "src/zone/zone-containers.h" + +namespace v8 { +namespace internal { +namespace compiler { + +LinearScheduler::LinearScheduler(Zone* zone, Graph* graph) + : graph_(graph), control_level_(zone), early_schedule_position_(zone) { + ComputeControlLevel(); +} + +void LinearScheduler::ComputeControlLevel() { + Node* start = graph_->start(); + SetControlLevel(start, 0); + + // Do BFS from the start node and compute the level of + // each control node. + std::queue queue({start}); + while (!queue.empty()) { + Node* node = queue.front(); + int level = GetControlLevel(node); + queue.pop(); + for (Edge const edge : node->use_edges()) { + if (!NodeProperties::IsControlEdge(edge)) continue; + Node* use = edge.from(); + if (control_level_.find(use) == control_level_.end() && + use->opcode() != IrOpcode::kEnd) { + SetControlLevel(use, level + 1); + queue.push(use); + } + } + } +} + +Node* LinearScheduler::GetEarlySchedulePosition(Node* node) { + DCHECK(!NodeProperties::IsControl(node)); + + auto it = early_schedule_position_.find(node); + if (it != early_schedule_position_.end()) return it->second; + + std::stack stack; + stack.push({node, nullptr, 0}); + Node* early_schedule_position = nullptr; + while (!stack.empty()) { + NodeState& top = stack.top(); + if (NodeProperties::IsPhi(top.node)) { + // For phi node, the early schedule position is its control node. + early_schedule_position = NodeProperties::GetControlInput(top.node); + } else if (top.node->InputCount() == 0) { + // For node without inputs, the early schedule position is start node. + early_schedule_position = graph_->start(); + } else { + // For others, the early schedule position is one of its inputs' early + // schedule position with maximal level. + if (top.input_index == top.node->InputCount()) { + // All inputs are visited, set early schedule position. + early_schedule_position = top.early_schedule_position; + } else { + // Visit top's input and find its early schedule position. + Node* input = top.node->InputAt(top.input_index); + Node* input_early_schedule_position = nullptr; + if (NodeProperties::IsControl(input)) { + input_early_schedule_position = input; + } else { + auto it = early_schedule_position_.find(input); + if (it != early_schedule_position_.end()) + input_early_schedule_position = it->second; + } + if (input_early_schedule_position != nullptr) { + if (top.early_schedule_position == nullptr || + GetControlLevel(top.early_schedule_position) < + GetControlLevel(input_early_schedule_position)) { + top.early_schedule_position = input_early_schedule_position; + } + top.input_index += 1; + } else { + top.input_index += 1; + stack.push({input, nullptr, 0}); + } + continue; + } + } + + // Found top's early schedule position, set it to the cache and pop it out + // of the stack. + SetEarlySchedulePosition(top.node, early_schedule_position); + stack.pop(); + // Update early schedule position of top's use. + if (!stack.empty()) { + NodeState& use = stack.top(); + if (use.early_schedule_position == nullptr || + GetControlLevel(use.early_schedule_position) < + GetControlLevel(top.early_schedule_position)) { + use.early_schedule_position = top.early_schedule_position; + } + } + } + + DCHECK(early_schedule_position != nullptr); + return early_schedule_position; +} + +bool LinearScheduler::SameBasicBlock(Node* node0, Node* node1) { + Node* early_schedule_position0 = NodeProperties::IsControl(node0) + ? node0 + : GetEarlySchedulePosition(node0); + Node* early_schedule_position1 = NodeProperties::IsControl(node1) + ? node1 + : GetEarlySchedulePosition(node1); + return early_schedule_position0 == early_schedule_position1; +} + +} // namespace compiler +} // namespace internal +} // namespace v8 diff --git a/src/compiler/linear-scheduler.h b/src/compiler/linear-scheduler.h new file mode 100644 index 0000000000..dc30aa042e --- /dev/null +++ b/src/compiler/linear-scheduler.h @@ -0,0 +1,67 @@ +// Copyright 2013 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_LINEAR_SCHEDULER_H_ +#define V8_COMPILER_LINEAR_SCHEDULER_H_ + +#include "src/base/flags.h" +#include "src/common/globals.h" +#include "src/compiler/node.h" +#include "src/compiler/opcodes.h" +#include "src/compiler/zone-stats.h" +#include "src/zone/zone-containers.h" + +namespace v8 { +namespace internal { +namespace compiler { + +// A simple, linear-time scheduler to check whether two nodes are in a same +// basic block without actually building basic block. +class V8_EXPORT_PRIVATE LinearScheduler { + public: + explicit LinearScheduler(Zone* zone, Graph* graph); + bool SameBasicBlock(Node* node0, Node* node1); + // Get a node's early schedule position. It is the earliest block (represented + // by a control node) where a node could be scheduled. + Node* GetEarlySchedulePosition(Node* node); + + private: + // Compute the level of each control node. The level is defined by the + // shortest control path from the start node. + void ComputeControlLevel(); + + struct NodeState { + Node* node; + Node* early_schedule_position; + int input_index; + }; + + int GetControlLevel(Node* control) const { + auto it = control_level_.find(control); + DCHECK(it != control_level_.end()); + return it->second; + } + + void SetControlLevel(Node* control, int level) { + DCHECK(control_level_.find(control) == control_level_.end()); + control_level_[control] = level; + } + + void SetEarlySchedulePosition(Node* node, Node* early_schedule_position) { + early_schedule_position_[node] = early_schedule_position; + } + + Graph* graph_; + // A map from a control node to the control level of the corresponding basic + // block. + ZoneMap control_level_; + // A map from a non-control node to its early schedule position. + ZoneMap early_schedule_position_; +}; + +} // namespace compiler +} // namespace internal +} // namespace v8 + +#endif // V8_COMPILER_LINEAR_SCHEDULER_H_ diff --git a/test/unittests/BUILD.gn b/test/unittests/BUILD.gn index 6f98293b1e..dbe3f2c38f 100644 --- a/test/unittests/BUILD.gn +++ b/test/unittests/BUILD.gn @@ -494,6 +494,10 @@ v8_source_set("unittests_sources") { ] } + if (v8_enable_wasm_simd256_revec) { + sources += [ "compiler/linear-scheduler-unittest.cc" ] + } + if (v8_enable_wasm_gdb_remote_debugging) { sources += [ "wasm/wasm-gdbserver-unittest.cc" ] } diff --git a/test/unittests/compiler/linear-scheduler-unittest.cc b/test/unittests/compiler/linear-scheduler-unittest.cc new file mode 100644 index 0000000000..1cc9e5529e --- /dev/null +++ b/test/unittests/compiler/linear-scheduler-unittest.cc @@ -0,0 +1,180 @@ +// 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/linear-scheduler.h" + +#include "src/compiler/access-builder.h" +#include "src/compiler/common-operator.h" +#include "src/compiler/graph.h" +#include "src/compiler/node.h" +#include "src/compiler/opcodes.h" +#include "src/compiler/operator.h" +#include "src/compiler/simplified-operator.h" +#include "test/unittests/compiler/compiler-test-utils.h" +#include "test/unittests/test-utils.h" +#include "testing/gmock/include/gmock/gmock.h" + +using testing::AnyOf; + +namespace v8 { +namespace internal { +namespace compiler { + +class LinearSchedulerTest : public TestWithIsolateAndZone { + public: + LinearSchedulerTest() + : TestWithIsolateAndZone(kCompressGraphZone), + graph_(zone()), + common_(zone()), + simplified_(zone()) {} + + Graph* graph() { return &graph_; } + CommonOperatorBuilder* common() { return &common_; } + SimplifiedOperatorBuilder* simplified() { return &simplified_; } + + private: + Graph graph_; + CommonOperatorBuilder common_; + SimplifiedOperatorBuilder simplified_; +}; + +namespace { + +const Operator kIntAdd(IrOpcode::kInt32Add, Operator::kPure, "Int32Add", 2, 0, + 0, 1, 0, 0); + +} // namespace + +TEST_F(LinearSchedulerTest, BuildSimpleScheduleEmpty) { + Node* start = graph()->NewNode(common()->Start(0)); + graph()->SetStart(start); + + Node* end = graph()->NewNode(common()->End(1), graph()->start()); + graph()->SetEnd(end); + + LinearScheduler simple_scheduler(zone(), graph()); + EXPECT_FALSE(simple_scheduler.SameBasicBlock(start, end)); +} + +TEST_F(LinearSchedulerTest, BuildSimpleScheduleOneParameter) { + graph()->SetStart(graph()->NewNode(common()->Start(0))); + + Node* p1 = graph()->NewNode(common()->Parameter(0), graph()->start()); + Node* zero = graph()->NewNode(common()->Int32Constant(0)); + Node* ret = graph()->NewNode(common()->Return(), zero, p1, graph()->start(), + graph()->start()); + + graph()->SetEnd(graph()->NewNode(common()->End(1), ret)); + + LinearScheduler simple_scheduler(zone(), graph()); + EXPECT_TRUE(simple_scheduler.SameBasicBlock(p1, zero)); + EXPECT_FALSE(simple_scheduler.SameBasicBlock(zero, ret)); +} + +TARGET_TEST_F(LinearSchedulerTest, FloatingDiamond) { + Node* start = graph()->NewNode(common()->Start(1)); + graph()->SetStart(start); + + Node* cond = graph()->NewNode(common()->Parameter(0), start); + Node* tv = graph()->NewNode(common()->Int32Constant(6)); + Node* fv = graph()->NewNode(common()->Int32Constant(7)); + Node* br = graph()->NewNode(common()->Branch(), cond, start); + Node* t = graph()->NewNode(common()->IfTrue(), br); + Node* f = graph()->NewNode(common()->IfFalse(), br); + Node* m = graph()->NewNode(common()->Merge(2), t, f); + Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + tv, fv, m); + Node* zero = graph()->NewNode(common()->Int32Constant(0)); + Node* ret = graph()->NewNode(common()->Return(), zero, phi, start, start); + Node* end = graph()->NewNode(common()->End(1), ret); + + graph()->SetEnd(end); + + LinearScheduler simple_scheduler(zone(), graph()); + EXPECT_FALSE(simple_scheduler.SameBasicBlock(t, f)); + EXPECT_FALSE(simple_scheduler.SameBasicBlock(phi, t)); + EXPECT_FALSE(simple_scheduler.SameBasicBlock(phi, f)); +} + +TARGET_TEST_F(LinearSchedulerTest, NestedFloatingDiamonds) { + Node* start = graph()->NewNode(common()->Start(2)); + graph()->SetStart(start); + + Node* p0 = graph()->NewNode(common()->Parameter(0), start); + + Node* tv = graph()->NewNode(common()->Int32Constant(7)); + Node* br = graph()->NewNode(common()->Branch(), p0, graph()->start()); + Node* t = graph()->NewNode(common()->IfTrue(), br); + Node* f = graph()->NewNode(common()->IfFalse(), br); + + Node* map = graph()->NewNode( + simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()), p0, p0, + start, f); + Node* br1 = graph()->NewNode(common()->Branch(), map, graph()->start()); + Node* t1 = graph()->NewNode(common()->IfTrue(), br1); + Node* f1 = graph()->NewNode(common()->IfFalse(), br1); + Node* m1 = graph()->NewNode(common()->Merge(2), t1, f1); + Node* ttrue = graph()->NewNode(common()->Int32Constant(1)); + Node* ffalse = graph()->NewNode(common()->Int32Constant(0)); + Node* phi1 = graph()->NewNode( + common()->Phi(MachineRepresentation::kTagged, 2), ttrue, ffalse, m1); + + Node* m = graph()->NewNode(common()->Merge(2), t, f); + Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + tv, phi1, m); + Node* ephi1 = graph()->NewNode(common()->EffectPhi(2), start, map, m); + + Node* zero = graph()->NewNode(common()->Int32Constant(0)); + Node* ret = graph()->NewNode(common()->Return(), zero, phi, ephi1, start); + Node* end = graph()->NewNode(common()->End(1), ret); + + graph()->SetEnd(end); + + LinearScheduler simple_scheduler(zone(), graph()); + EXPECT_TRUE(simple_scheduler.SameBasicBlock(map, f)); + EXPECT_FALSE(simple_scheduler.SameBasicBlock(map, br1)); + EXPECT_TRUE(simple_scheduler.SameBasicBlock(ephi1, phi)); +} + +TARGET_TEST_F(LinearSchedulerTest, LoopedFloatingDiamond) { + Node* start = graph()->NewNode(common()->Start(2)); + graph()->SetStart(start); + + Node* p0 = graph()->NewNode(common()->Parameter(0), start); + + Node* c = graph()->NewNode(common()->Int32Constant(7)); + Node* loop = graph()->NewNode(common()->Loop(2), start, start); + Node* ind = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), + p0, p0, loop); + Node* add = graph()->NewNode(&kIntAdd, ind, c); + + Node* br = graph()->NewNode(common()->Branch(), add, loop); + Node* t = graph()->NewNode(common()->IfTrue(), br); + Node* f = graph()->NewNode(common()->IfFalse(), br); + + Node* br1 = graph()->NewNode(common()->Branch(), p0, graph()->start()); + Node* t1 = graph()->NewNode(common()->IfTrue(), br1); + Node* f1 = graph()->NewNode(common()->IfFalse(), br1); + Node* m1 = graph()->NewNode(common()->Merge(2), t1, f1); + Node* phi1 = graph()->NewNode( + common()->Phi(MachineRepresentation::kTagged, 2), add, p0, m1); + + loop->ReplaceInput(1, t); // close loop. + ind->ReplaceInput(1, phi1); // close induction variable. + + Node* zero = graph()->NewNode(common()->Int32Constant(0)); + Node* ret = graph()->NewNode(common()->Return(), zero, ind, start, f); + Node* end = graph()->NewNode(common()->End(2), ret, f); + + graph()->SetEnd(end); + + LinearScheduler simple_scheduler(zone(), graph()); + EXPECT_TRUE(simple_scheduler.SameBasicBlock(ind, loop)); + EXPECT_TRUE(simple_scheduler.SameBasicBlock(phi1, m1)); + EXPECT_FALSE(simple_scheduler.SameBasicBlock(loop, m1)); +} + +} // namespace compiler +} // namespace internal +} // namespace v8