2b93b8aa41
This simplifies the handling of the End node. Based on this CL we will finally fix terminating every loop from the beginning (via Terminate nodes) and fix inlining of Throw, Deoptimize and Terminate. R=mstarzinger@chromium.org Review URL: https://codereview.chromium.org/1157023002 Cr-Commit-Position: refs/heads/master@{#28620}
596 lines
19 KiB
C++
596 lines
19 KiB
C++
// 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/codegen.h"
|
|
#include "src/compiler/all-nodes.h"
|
|
#include "src/compiler/common-operator.h"
|
|
#include "src/compiler/diamond.h"
|
|
#include "src/compiler/graph.h"
|
|
#include "src/compiler/js-graph.h"
|
|
#include "src/compiler/js-operator.h"
|
|
#include "src/compiler/operator.h"
|
|
#include "src/compiler/osr.h"
|
|
#include "test/cctest/cctest.h"
|
|
|
|
using namespace v8::internal;
|
|
using namespace v8::internal::compiler;
|
|
|
|
// TODO(titzer): move this method to a common testing place.
|
|
|
|
static int CheckInputs(Node* node, Node* i0 = NULL, Node* i1 = NULL,
|
|
Node* i2 = NULL, Node* i3 = NULL) {
|
|
int count = 4;
|
|
if (i3 == NULL) count = 3;
|
|
if (i2 == NULL) count = 2;
|
|
if (i1 == NULL) count = 1;
|
|
if (i0 == NULL) count = 0;
|
|
CHECK_EQ(count, node->InputCount());
|
|
if (i0 != NULL) CHECK_EQ(i0, node->InputAt(0));
|
|
if (i1 != NULL) CHECK_EQ(i1, node->InputAt(1));
|
|
if (i2 != NULL) CHECK_EQ(i2, node->InputAt(2));
|
|
if (i3 != NULL) CHECK_EQ(i3, node->InputAt(3));
|
|
return count;
|
|
}
|
|
|
|
|
|
static Operator kIntLt(IrOpcode::kInt32LessThan, Operator::kPure,
|
|
"Int32LessThan", 2, 0, 0, 1, 0, 0);
|
|
static Operator kIntAdd(IrOpcode::kInt32Add, Operator::kPure, "Int32Add", 2, 0,
|
|
0, 1, 0, 0);
|
|
|
|
|
|
static const int kMaxOsrValues = 10;
|
|
|
|
class OsrDeconstructorTester : public HandleAndZoneScope {
|
|
public:
|
|
explicit OsrDeconstructorTester(int num_values)
|
|
: isolate(main_isolate()),
|
|
common(main_zone()),
|
|
graph(main_zone()),
|
|
jsgraph(main_isolate(), &graph, &common, NULL, NULL),
|
|
start(graph.NewNode(common.Start(1))),
|
|
p0(graph.NewNode(common.Parameter(0), start)),
|
|
end(graph.NewNode(common.End(1), start)),
|
|
osr_normal_entry(graph.NewNode(common.OsrNormalEntry(), start, start)),
|
|
osr_loop_entry(graph.NewNode(common.OsrLoopEntry(), start, start)),
|
|
self(graph.NewNode(common.Int32Constant(0xaabbccdd))) {
|
|
CHECK(num_values <= kMaxOsrValues);
|
|
graph.SetStart(start);
|
|
for (int i = 0; i < num_values; i++) {
|
|
osr_values[i] = graph.NewNode(common.OsrValue(i), osr_loop_entry);
|
|
}
|
|
}
|
|
|
|
Isolate* isolate;
|
|
CommonOperatorBuilder common;
|
|
Graph graph;
|
|
JSGraph jsgraph;
|
|
Node* start;
|
|
Node* p0;
|
|
Node* end;
|
|
Node* osr_normal_entry;
|
|
Node* osr_loop_entry;
|
|
Node* self;
|
|
Node* osr_values[kMaxOsrValues];
|
|
|
|
Node* NewOsrPhi(Node* loop, Node* incoming, int osr_value, Node* back1 = NULL,
|
|
Node* back2 = NULL, Node* back3 = NULL) {
|
|
int count = 5;
|
|
if (back3 == NULL) count = 4;
|
|
if (back2 == NULL) count = 3;
|
|
if (back1 == NULL) count = 2;
|
|
CHECK_EQ(loop->InputCount(), count);
|
|
CHECK_EQ(osr_loop_entry, loop->InputAt(1));
|
|
|
|
Node* inputs[6];
|
|
inputs[0] = incoming;
|
|
inputs[1] = osr_values[osr_value];
|
|
if (count > 2) inputs[2] = back1;
|
|
if (count > 3) inputs[3] = back2;
|
|
if (count > 4) inputs[4] = back3;
|
|
inputs[count] = loop;
|
|
return graph.NewNode(common.Phi(kMachAnyTagged, count), count + 1, inputs);
|
|
}
|
|
|
|
Node* NewLoop(bool is_osr, int num_backedges, Node* entry = NULL) {
|
|
CHECK_LT(num_backedges, 4);
|
|
CHECK_GE(num_backedges, 0);
|
|
int count = 1 + num_backedges;
|
|
if (entry == NULL) entry = osr_normal_entry;
|
|
Node* inputs[5] = {entry, self, self, self, self};
|
|
if (is_osr) {
|
|
count = 2 + num_backedges;
|
|
inputs[1] = osr_loop_entry;
|
|
}
|
|
|
|
Node* loop = graph.NewNode(common.Loop(count), count, inputs);
|
|
for (int i = 0; i < loop->InputCount(); i++) {
|
|
if (loop->InputAt(i) == self) loop->ReplaceInput(i, loop);
|
|
}
|
|
|
|
return loop;
|
|
}
|
|
|
|
Node* NewOsrLoop(int num_backedges, Node* entry = NULL) {
|
|
return NewLoop(true, num_backedges, entry);
|
|
}
|
|
|
|
void DeconstructOsr() {
|
|
OsrHelper helper(0, 0);
|
|
helper.Deconstruct(&jsgraph, &common, main_zone());
|
|
AllNodes nodes(main_zone(), &graph);
|
|
// Should be edited out.
|
|
CHECK(!nodes.IsLive(osr_normal_entry));
|
|
CHECK(!nodes.IsLive(osr_loop_entry));
|
|
// No dangling nodes should be left over.
|
|
for (Node* const node : nodes.live) {
|
|
for (Node* const use : node->uses()) {
|
|
CHECK(std::find(nodes.live.begin(), nodes.live.end(), use) !=
|
|
nodes.live.end());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
TEST(Deconstruct_osr0) {
|
|
OsrDeconstructorTester T(0);
|
|
|
|
Node* loop = T.NewOsrLoop(1);
|
|
|
|
T.graph.SetEnd(loop);
|
|
|
|
T.DeconstructOsr();
|
|
|
|
CheckInputs(loop, T.start, loop);
|
|
}
|
|
|
|
|
|
TEST(Deconstruct_osr1) {
|
|
OsrDeconstructorTester T(1);
|
|
|
|
Node* loop = T.NewOsrLoop(1);
|
|
Node* osr_phi =
|
|
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant());
|
|
|
|
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, loop);
|
|
T.graph.SetEnd(ret);
|
|
|
|
T.DeconstructOsr();
|
|
|
|
CheckInputs(loop, T.start, loop);
|
|
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), loop);
|
|
CheckInputs(ret, osr_phi, T.start, loop);
|
|
}
|
|
|
|
|
|
TEST(Deconstruct_osr1_type) {
|
|
OsrDeconstructorTester T(1);
|
|
|
|
Node* loop = T.NewOsrLoop(1);
|
|
Node* osr_phi =
|
|
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant());
|
|
Type* type = Type::Signed32();
|
|
NodeProperties::SetBounds(osr_phi, Bounds(type, type));
|
|
|
|
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, loop);
|
|
T.graph.SetEnd(ret);
|
|
|
|
OsrHelper helper(0, 0);
|
|
helper.Deconstruct(&T.jsgraph, &T.common, T.main_zone());
|
|
|
|
CHECK_EQ(type, NodeProperties::GetBounds(T.osr_values[0]).lower);
|
|
CHECK_EQ(type, NodeProperties::GetBounds(T.osr_values[0]).upper);
|
|
|
|
CheckInputs(loop, T.start, loop);
|
|
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), loop);
|
|
CheckInputs(ret, osr_phi, T.start, loop);
|
|
}
|
|
|
|
|
|
TEST(Deconstruct_osr_remove_prologue) {
|
|
OsrDeconstructorTester T(1);
|
|
Diamond d(&T.graph, &T.common, T.p0);
|
|
d.Chain(T.osr_normal_entry);
|
|
|
|
Node* loop = T.NewOsrLoop(1, d.merge);
|
|
Node* osr_phi =
|
|
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant());
|
|
|
|
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, loop);
|
|
T.graph.SetEnd(ret);
|
|
|
|
T.DeconstructOsr();
|
|
|
|
CheckInputs(loop, T.start, loop);
|
|
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), loop);
|
|
CheckInputs(ret, osr_phi, T.start, loop);
|
|
|
|
// The control before the loop should have been removed.
|
|
AllNodes nodes(T.main_zone(), &T.graph);
|
|
CHECK(!nodes.IsLive(d.branch));
|
|
CHECK(!nodes.IsLive(d.if_true));
|
|
CHECK(!nodes.IsLive(d.if_false));
|
|
CHECK(!nodes.IsLive(d.merge));
|
|
}
|
|
|
|
|
|
TEST(Deconstruct_osr_with_body1) {
|
|
OsrDeconstructorTester T(1);
|
|
|
|
Node* loop = T.NewOsrLoop(1);
|
|
|
|
Node* branch = T.graph.NewNode(T.common.Branch(), T.p0, loop);
|
|
Node* if_true = T.graph.NewNode(T.common.IfTrue(), branch);
|
|
Node* if_false = T.graph.NewNode(T.common.IfFalse(), branch);
|
|
loop->ReplaceInput(2, if_true);
|
|
|
|
Node* osr_phi =
|
|
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant());
|
|
|
|
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, if_false);
|
|
T.graph.SetEnd(ret);
|
|
|
|
T.DeconstructOsr();
|
|
|
|
CheckInputs(loop, T.start, if_true);
|
|
CheckInputs(branch, T.p0, loop);
|
|
CheckInputs(if_true, branch);
|
|
CheckInputs(if_false, branch);
|
|
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), loop);
|
|
CheckInputs(ret, osr_phi, T.start, if_false);
|
|
}
|
|
|
|
|
|
TEST(Deconstruct_osr_with_body2) {
|
|
OsrDeconstructorTester T(1);
|
|
|
|
Node* loop = T.NewOsrLoop(1);
|
|
|
|
// Two chained branches in the the body of the loop.
|
|
Node* branch1 = T.graph.NewNode(T.common.Branch(), T.p0, loop);
|
|
Node* if_true1 = T.graph.NewNode(T.common.IfTrue(), branch1);
|
|
Node* if_false1 = T.graph.NewNode(T.common.IfFalse(), branch1);
|
|
|
|
Node* branch2 = T.graph.NewNode(T.common.Branch(), T.p0, if_true1);
|
|
Node* if_true2 = T.graph.NewNode(T.common.IfTrue(), branch2);
|
|
Node* if_false2 = T.graph.NewNode(T.common.IfFalse(), branch2);
|
|
loop->ReplaceInput(2, if_true2);
|
|
|
|
Node* osr_phi =
|
|
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant());
|
|
|
|
Node* merge = T.graph.NewNode(T.common.Merge(2), if_false1, if_false2);
|
|
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, merge);
|
|
T.graph.SetEnd(ret);
|
|
|
|
T.DeconstructOsr();
|
|
|
|
CheckInputs(loop, T.start, if_true2);
|
|
CheckInputs(branch1, T.p0, loop);
|
|
CheckInputs(branch2, T.p0, if_true1);
|
|
CheckInputs(if_true1, branch1);
|
|
CheckInputs(if_false1, branch1);
|
|
CheckInputs(if_true2, branch2);
|
|
CheckInputs(if_false2, branch2);
|
|
|
|
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), loop);
|
|
CheckInputs(ret, osr_phi, T.start, merge);
|
|
CheckInputs(merge, if_false1, if_false2);
|
|
}
|
|
|
|
|
|
TEST(Deconstruct_osr_with_body3) {
|
|
OsrDeconstructorTester T(1);
|
|
|
|
Node* loop = T.NewOsrLoop(2);
|
|
|
|
// Two branches that create two different backedges.
|
|
Node* branch1 = T.graph.NewNode(T.common.Branch(), T.p0, loop);
|
|
Node* if_true1 = T.graph.NewNode(T.common.IfTrue(), branch1);
|
|
Node* if_false1 = T.graph.NewNode(T.common.IfFalse(), branch1);
|
|
|
|
Node* branch2 = T.graph.NewNode(T.common.Branch(), T.p0, if_true1);
|
|
Node* if_true2 = T.graph.NewNode(T.common.IfTrue(), branch2);
|
|
Node* if_false2 = T.graph.NewNode(T.common.IfFalse(), branch2);
|
|
loop->ReplaceInput(2, if_false1);
|
|
loop->ReplaceInput(3, if_true2);
|
|
|
|
Node* osr_phi =
|
|
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant(),
|
|
T.jsgraph.ZeroConstant());
|
|
|
|
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, if_false2);
|
|
T.graph.SetEnd(ret);
|
|
|
|
T.DeconstructOsr();
|
|
|
|
CheckInputs(loop, T.start, if_false1, if_true2);
|
|
CheckInputs(branch1, T.p0, loop);
|
|
CheckInputs(branch2, T.p0, if_true1);
|
|
CheckInputs(if_true1, branch1);
|
|
CheckInputs(if_false1, branch1);
|
|
CheckInputs(if_true2, branch2);
|
|
CheckInputs(if_false2, branch2);
|
|
|
|
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(),
|
|
T.jsgraph.ZeroConstant(), loop);
|
|
CheckInputs(ret, osr_phi, T.start, if_false2);
|
|
}
|
|
|
|
|
|
struct While {
|
|
OsrDeconstructorTester& t;
|
|
Node* branch;
|
|
Node* if_true;
|
|
Node* exit;
|
|
Node* loop;
|
|
|
|
While(OsrDeconstructorTester& R, Node* cond, bool is_osr, int backedges = 1)
|
|
: t(R) {
|
|
loop = t.NewLoop(is_osr, backedges);
|
|
branch = t.graph.NewNode(t.common.Branch(), cond, loop);
|
|
if_true = t.graph.NewNode(t.common.IfTrue(), branch);
|
|
exit = t.graph.NewNode(t.common.IfFalse(), branch);
|
|
loop->ReplaceInput(loop->InputCount() - 1, if_true);
|
|
}
|
|
|
|
void Nest(While& that) {
|
|
that.loop->ReplaceInput(that.loop->InputCount() - 1, exit);
|
|
this->loop->ReplaceInput(0, that.if_true);
|
|
}
|
|
|
|
Node* Phi(Node* i1, Node* i2, Node* i3) {
|
|
if (loop->InputCount() == 2) {
|
|
return t.graph.NewNode(t.common.Phi(kMachAnyTagged, 2), i1, i2, loop);
|
|
} else {
|
|
return t.graph.NewNode(t.common.Phi(kMachAnyTagged, 3), i1, i2, i3, loop);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
static Node* FindSuccessor(Node* node, IrOpcode::Value opcode) {
|
|
for (Node* use : node->uses()) {
|
|
if (use->opcode() == opcode) return use;
|
|
}
|
|
UNREACHABLE(); // should have been found.
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
TEST(Deconstruct_osr_nested1) {
|
|
OsrDeconstructorTester T(1);
|
|
|
|
While outer(T, T.p0, false);
|
|
While inner(T, T.p0, true);
|
|
inner.Nest(outer);
|
|
|
|
Node* outer_phi = outer.Phi(T.p0, T.p0, nullptr);
|
|
outer.branch->ReplaceInput(0, outer_phi);
|
|
|
|
Node* osr_phi = inner.Phi(T.jsgraph.OneConstant(), T.osr_values[0],
|
|
T.jsgraph.ZeroConstant());
|
|
inner.branch->ReplaceInput(0, osr_phi);
|
|
outer_phi->ReplaceInput(1, osr_phi);
|
|
|
|
Node* ret =
|
|
T.graph.NewNode(T.common.Return(), outer_phi, T.start, outer.exit);
|
|
Node* end = T.graph.NewNode(T.common.End(1), ret);
|
|
T.graph.SetEnd(end);
|
|
|
|
T.DeconstructOsr();
|
|
|
|
// Check structure of deconstructed graph.
|
|
// Check inner OSR loop is directly connected to start.
|
|
CheckInputs(inner.loop, T.start, inner.if_true);
|
|
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), inner.loop);
|
|
|
|
// Check control transfer to copy of outer loop.
|
|
Node* new_outer_loop = FindSuccessor(inner.exit, IrOpcode::kLoop);
|
|
Node* new_outer_phi = FindSuccessor(new_outer_loop, IrOpcode::kPhi);
|
|
CHECK_NE(new_outer_loop, outer.loop);
|
|
CHECK_NE(new_outer_phi, outer_phi);
|
|
|
|
CheckInputs(new_outer_loop, inner.exit, new_outer_loop->InputAt(1));
|
|
|
|
// Check structure of outer loop.
|
|
Node* new_outer_branch = FindSuccessor(new_outer_loop, IrOpcode::kBranch);
|
|
CHECK_NE(new_outer_branch, outer.branch);
|
|
CheckInputs(new_outer_branch, new_outer_phi, new_outer_loop);
|
|
Node* new_outer_exit = FindSuccessor(new_outer_branch, IrOpcode::kIfFalse);
|
|
Node* new_outer_if_true = FindSuccessor(new_outer_branch, IrOpcode::kIfTrue);
|
|
|
|
// Check structure of return.
|
|
end = T.graph.end();
|
|
Node* new_ret = end->InputAt(0);
|
|
CHECK_EQ(IrOpcode::kReturn, new_ret->opcode());
|
|
CheckInputs(new_ret, new_outer_phi, T.start, new_outer_exit);
|
|
|
|
// Check structure of inner loop.
|
|
Node* new_inner_loop = FindSuccessor(new_outer_if_true, IrOpcode::kLoop);
|
|
Node* new_inner_phi = FindSuccessor(new_inner_loop, IrOpcode::kPhi);
|
|
|
|
CheckInputs(new_inner_phi, T.jsgraph.OneConstant(), T.jsgraph.ZeroConstant(),
|
|
new_inner_loop);
|
|
CheckInputs(new_outer_phi, osr_phi, new_inner_phi, new_outer_loop);
|
|
}
|
|
|
|
|
|
TEST(Deconstruct_osr_nested2) {
|
|
OsrDeconstructorTester T(1);
|
|
|
|
// Test multiple backedge outer loop.
|
|
While outer(T, T.p0, false, 2);
|
|
While inner(T, T.p0, true);
|
|
inner.Nest(outer);
|
|
|
|
Node* outer_phi = outer.Phi(T.p0, T.p0, T.p0);
|
|
outer.branch->ReplaceInput(0, outer_phi);
|
|
|
|
Node* osr_phi = inner.Phi(T.jsgraph.OneConstant(), T.osr_values[0],
|
|
T.jsgraph.ZeroConstant());
|
|
inner.branch->ReplaceInput(0, osr_phi);
|
|
outer_phi->ReplaceInput(1, osr_phi);
|
|
outer_phi->ReplaceInput(2, T.jsgraph.ZeroConstant());
|
|
|
|
Node* x_branch = T.graph.NewNode(T.common.Branch(), osr_phi, inner.exit);
|
|
Node* x_true = T.graph.NewNode(T.common.IfTrue(), x_branch);
|
|
Node* x_false = T.graph.NewNode(T.common.IfFalse(), x_branch);
|
|
|
|
outer.loop->ReplaceInput(1, x_true);
|
|
outer.loop->ReplaceInput(2, x_false);
|
|
|
|
Node* ret =
|
|
T.graph.NewNode(T.common.Return(), outer_phi, T.start, outer.exit);
|
|
Node* end = T.graph.NewNode(T.common.End(1), ret);
|
|
T.graph.SetEnd(end);
|
|
|
|
T.DeconstructOsr();
|
|
|
|
// Check structure of deconstructed graph.
|
|
// Check inner OSR loop is directly connected to start.
|
|
CheckInputs(inner.loop, T.start, inner.if_true);
|
|
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), inner.loop);
|
|
|
|
// Check control transfer to copy of outer loop.
|
|
Node* new_merge = FindSuccessor(x_true, IrOpcode::kMerge);
|
|
CHECK_EQ(new_merge, FindSuccessor(x_false, IrOpcode::kMerge));
|
|
CheckInputs(new_merge, x_true, x_false);
|
|
|
|
Node* new_outer_loop = FindSuccessor(new_merge, IrOpcode::kLoop);
|
|
Node* new_outer_phi = FindSuccessor(new_outer_loop, IrOpcode::kPhi);
|
|
CHECK_NE(new_outer_loop, outer.loop);
|
|
CHECK_NE(new_outer_phi, outer_phi);
|
|
|
|
Node* new_entry_phi = FindSuccessor(new_merge, IrOpcode::kPhi);
|
|
CheckInputs(new_entry_phi, osr_phi, T.jsgraph.ZeroConstant(), new_merge);
|
|
|
|
CHECK_EQ(new_merge, new_outer_loop->InputAt(0));
|
|
|
|
// Check structure of outer loop.
|
|
Node* new_outer_branch = FindSuccessor(new_outer_loop, IrOpcode::kBranch);
|
|
CHECK_NE(new_outer_branch, outer.branch);
|
|
CheckInputs(new_outer_branch, new_outer_phi, new_outer_loop);
|
|
Node* new_outer_exit = FindSuccessor(new_outer_branch, IrOpcode::kIfFalse);
|
|
Node* new_outer_if_true = FindSuccessor(new_outer_branch, IrOpcode::kIfTrue);
|
|
|
|
// Check structure of return.
|
|
end = T.graph.end();
|
|
Node* new_ret = end->InputAt(0);
|
|
CHECK_EQ(IrOpcode::kReturn, new_ret->opcode());
|
|
CheckInputs(new_ret, new_outer_phi, T.start, new_outer_exit);
|
|
|
|
// Check structure of inner loop.
|
|
Node* new_inner_loop = FindSuccessor(new_outer_if_true, IrOpcode::kLoop);
|
|
Node* new_inner_phi = FindSuccessor(new_inner_loop, IrOpcode::kPhi);
|
|
|
|
CheckInputs(new_inner_phi, T.jsgraph.OneConstant(), T.jsgraph.ZeroConstant(),
|
|
new_inner_loop);
|
|
CheckInputs(new_outer_phi, new_entry_phi, new_inner_phi,
|
|
T.jsgraph.ZeroConstant(), new_outer_loop);
|
|
}
|
|
|
|
|
|
Node* MakeCounter(JSGraph* jsgraph, Node* start, Node* loop) {
|
|
int count = loop->InputCount();
|
|
NodeVector tmp_inputs(jsgraph->graph()->zone());
|
|
for (int i = 0; i < count; i++) {
|
|
tmp_inputs.push_back(start);
|
|
}
|
|
tmp_inputs.push_back(loop);
|
|
|
|
Node* phi = jsgraph->graph()->NewNode(
|
|
jsgraph->common()->Phi(kMachInt32, count), count + 1, &tmp_inputs[0]);
|
|
Node* inc = jsgraph->graph()->NewNode(&kIntAdd, phi, jsgraph->OneConstant());
|
|
|
|
for (int i = 1; i < count; i++) {
|
|
phi->ReplaceInput(i, inc);
|
|
}
|
|
return phi;
|
|
}
|
|
|
|
|
|
TEST(Deconstruct_osr_nested3) {
|
|
OsrDeconstructorTester T(1);
|
|
|
|
// outermost loop.
|
|
While loop0(T, T.p0, false, 1);
|
|
Node* loop0_cntr = MakeCounter(&T.jsgraph, T.p0, loop0.loop);
|
|
loop0.branch->ReplaceInput(0, loop0_cntr);
|
|
|
|
// middle loop.
|
|
Node* loop1 = T.graph.NewNode(T.common.Loop(2), loop0.if_true, T.self);
|
|
loop1->ReplaceInput(0, loop0.if_true);
|
|
Node* loop1_phi = T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), loop0_cntr,
|
|
loop0_cntr, loop1);
|
|
|
|
// innermost (OSR) loop.
|
|
While loop2(T, T.p0, true, 1);
|
|
loop2.loop->ReplaceInput(0, loop1);
|
|
|
|
Node* loop2_cntr = MakeCounter(&T.jsgraph, loop1_phi, loop2.loop);
|
|
loop2_cntr->ReplaceInput(1, T.osr_values[0]);
|
|
Node* osr_phi = loop2_cntr;
|
|
Node* loop2_inc = loop2_cntr->InputAt(2);
|
|
loop2.branch->ReplaceInput(0, loop2_cntr);
|
|
|
|
loop1_phi->ReplaceInput(1, loop2_cntr);
|
|
loop0_cntr->ReplaceInput(1, loop2_cntr);
|
|
|
|
// Branch to either the outer or middle loop.
|
|
Node* branch = T.graph.NewNode(T.common.Branch(), loop2_cntr, loop2.exit);
|
|
Node* if_true = T.graph.NewNode(T.common.IfTrue(), branch);
|
|
Node* if_false = T.graph.NewNode(T.common.IfFalse(), branch);
|
|
|
|
loop0.loop->ReplaceInput(1, if_true);
|
|
loop1->ReplaceInput(1, if_false);
|
|
|
|
Node* ret =
|
|
T.graph.NewNode(T.common.Return(), loop0_cntr, T.start, loop0.exit);
|
|
Node* end = T.graph.NewNode(T.common.End(1), ret);
|
|
T.graph.SetEnd(end);
|
|
|
|
T.DeconstructOsr();
|
|
|
|
// Check structure of deconstructed graph.
|
|
// Check loop2 (OSR loop) is directly connected to start.
|
|
CheckInputs(loop2.loop, T.start, loop2.if_true);
|
|
CheckInputs(osr_phi, T.osr_values[0], loop2_inc, loop2.loop);
|
|
CheckInputs(loop2.branch, osr_phi, loop2.loop);
|
|
CheckInputs(loop2.if_true, loop2.branch);
|
|
CheckInputs(loop2.exit, loop2.branch);
|
|
CheckInputs(branch, osr_phi, loop2.exit);
|
|
CheckInputs(if_true, branch);
|
|
CheckInputs(if_false, branch);
|
|
|
|
// Check structure of new_loop1.
|
|
Node* new_loop1_loop = FindSuccessor(if_false, IrOpcode::kLoop);
|
|
// TODO(titzer): check the internal copy of loop2.
|
|
USE(new_loop1_loop);
|
|
|
|
// Check structure of new_loop0.
|
|
Node* new_loop0_loop_entry = FindSuccessor(if_true, IrOpcode::kMerge);
|
|
Node* new_loop0_loop = FindSuccessor(new_loop0_loop_entry, IrOpcode::kLoop);
|
|
// TODO(titzer): check the internal copies of loop1 and loop2.
|
|
|
|
Node* new_loop0_branch = FindSuccessor(new_loop0_loop, IrOpcode::kBranch);
|
|
Node* new_loop0_if_true = FindSuccessor(new_loop0_branch, IrOpcode::kIfTrue);
|
|
Node* new_loop0_exit = FindSuccessor(new_loop0_branch, IrOpcode::kIfFalse);
|
|
|
|
USE(new_loop0_if_true);
|
|
|
|
Node* new_ret = T.graph.end()->InputAt(0);
|
|
CHECK_EQ(IrOpcode::kReturn, new_ret->opcode());
|
|
|
|
Node* new_loop0_phi = new_ret->InputAt(0);
|
|
CHECK_EQ(IrOpcode::kPhi, new_loop0_phi->opcode());
|
|
CHECK_EQ(new_loop0_loop, NodeProperties::GetControlInput(new_loop0_phi));
|
|
CHECK_EQ(new_loop0_phi, FindSuccessor(new_loop0_loop, IrOpcode::kPhi));
|
|
|
|
// Check that the return returns the phi from the OSR loop and control
|
|
// depends on the copy of the outer loop0.
|
|
CheckInputs(new_ret, new_loop0_phi, T.graph.start(), new_loop0_exit);
|
|
}
|