Implement graph trimming in ControlReducer.
authortitzer@chromium.org <titzer@chromium.org>
Fri, 17 Oct 2014 11:51:57 +0000 (11:51 +0000)
committertitzer@chromium.org <titzer@chromium.org>
Fri, 17 Oct 2014 11:51:57 +0000 (11:51 +0000)
Trimming the graph consists of breaking links from nodes that are not reachable from end to nodes that are reachable from end. Such dead nodes show up in the use lists of the live nodes and though mostly harmless, just clutter up the graph. They also can limit instruction selection opportunities, so it is good to get rid of them.

This CL is one half of the ControlReducer functionality, the other half
being branch folding.

R=bmeurer@chromium.org
BUG=

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24694 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

15 files changed:
BUILD.gn
src/compiler/control-reducer.cc [new file with mode: 0644]
src/compiler/control-reducer.h [new file with mode: 0644]
src/compiler/pipeline.cc
test/cctest/cctest.gyp
test/cctest/cctest.status
test/cctest/compiler/test-control-reducer.cc [new file with mode: 0644]
test/cctest/compiler/test-run-jsbranches.cc
test/mjsunit/asm/do-while-false.js [new file with mode: 0644]
test/mjsunit/asm/if-folding.js [new file with mode: 0644]
test/mjsunit/asm/if-reduction.js [new file with mode: 0644]
test/mjsunit/asm/infinite-loops-taken.js [new file with mode: 0644]
test/mjsunit/asm/infinite-loops.js [new file with mode: 0644]
test/mjsunit/mjsunit.status
tools/gyp/v8.gyp

index f736901..08dd231 100644 (file)
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -487,6 +487,8 @@ source_set("v8_base") {
     "src/compiler/common-operator.h",
     "src/compiler/control-builders.cc",
     "src/compiler/control-builders.h",
+    "src/compiler/control-reducer.cc",
+    "src/compiler/control-reducer.h",
     "src/compiler/frame.h",
     "src/compiler/gap-resolver.cc",
     "src/compiler/gap-resolver.h",
diff --git a/src/compiler/control-reducer.cc b/src/compiler/control-reducer.cc
new file mode 100644 (file)
index 0000000..23bf929
--- /dev/null
@@ -0,0 +1,122 @@
+// Copyright 2014 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/common-operator.h"
+#include "src/compiler/control-reducer.h"
+#include "src/compiler/graph.h"
+#include "src/compiler/js-graph.h"
+#include "src/compiler/node-matchers.h"
+#include "src/compiler/node-properties-inl.h"
+#include "src/zone-containers.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+enum VisitState { kUnvisited, kOnStack, kRevisit, kVisited };
+
+#define TRACE(x) \
+  if (FLAG_trace_turbo) PrintF x
+
+class ControlReducerImpl {
+ public:
+  ControlReducerImpl(JSGraph* jsgraph, CommonOperatorBuilder* common)
+      : zone_(jsgraph->zone()->isolate()),
+        jsgraph_(jsgraph),
+        common_(common),
+        state_(jsgraph->graph()->NodeCount(), kUnvisited, &zone_),
+        stack_(&zone_),
+        revisit_(&zone_),
+        dead_(NULL) {}
+
+  Zone zone_;
+  JSGraph* jsgraph_;
+  CommonOperatorBuilder* common_;
+  ZoneVector<VisitState> state_;
+  ZoneDeque<Node*> stack_;
+  ZoneDeque<Node*> revisit_;
+  Node* dead_;
+
+  void Trim() {
+    //  Mark all nodes reachable from end.
+    NodeVector nodes(&zone_);
+    state_.assign(jsgraph_->graph()->NodeCount(), kUnvisited);
+    Push(jsgraph_->graph()->end());
+    while (!stack_.empty()) {
+      Node* node = stack_[stack_.size() - 1];
+      stack_.pop_back();
+      state_[node->id()] = kVisited;
+      nodes.push_back(node);
+      for (InputIter i = node->inputs().begin(); i != node->inputs().end();
+           ++i) {
+        Recurse(*i);  // pushes node onto the stack if necessary.
+      }
+    }
+    // Process cached nodes in the JSGraph too.
+    jsgraph_->GetCachedNodes(&nodes);
+    // Remove dead->live edges.
+    for (size_t j = 0; j < nodes.size(); j++) {
+      Node* node = nodes[j];
+      for (UseIter i = node->uses().begin(); i != node->uses().end();) {
+        size_t id = static_cast<size_t>((*i)->id());
+        if (state_[id] != kVisited) {
+          TRACE(("DeadLink: #%d:%s(%d) -> #%d:%s\n", (*i)->id(),
+                 (*i)->op()->mnemonic(), i.index(), node->id(),
+                 node->op()->mnemonic()));
+          i.UpdateToAndIncrement(NULL);
+        } else {
+          ++i;
+        }
+      }
+    }
+#if DEBUG
+    // Verify that no inputs to live nodes are NULL.
+    for (size_t j = 0; j < nodes.size(); j++) {
+      Node* node = nodes[j];
+      for (InputIter i = node->inputs().begin(); i != node->inputs().end();
+           ++i) {
+        CHECK_NE(NULL, *i);
+      }
+      for (UseIter i = node->uses().begin(); i != node->uses().end(); ++i) {
+        size_t id = static_cast<size_t>((*i)->id());
+        CHECK_EQ(kVisited, state_[id]);
+      }
+    }
+#endif
+  }
+
+  // Push a node onto the stack if its state is {kUnvisited} or {kRevisit}.
+  bool Recurse(Node* node) {
+    size_t id = static_cast<size_t>(node->id());
+    if (id < state_.size()) {
+      if (state_[id] != kRevisit && state_[id] != kUnvisited) return false;
+    } else {
+      state_.resize((3 * id) / 2, kUnvisited);
+    }
+    Push(node);
+    return true;
+  }
+
+  void Push(Node* node) {
+    state_[node->id()] = kOnStack;
+    stack_.push_back(node);
+  }
+};
+
+void ControlReducer::ReduceGraph(JSGraph* jsgraph,
+                                 CommonOperatorBuilder* common) {
+  ControlReducerImpl impl(jsgraph, NULL);
+  // Only trim the graph for now. Control reduction can reduce non-terminating
+  // loops to graphs that are unschedulable at the moment.
+  impl.Trim();
+}
+
+
+void ControlReducer::TrimGraph(JSGraph* jsgraph) {
+  ControlReducerImpl impl(jsgraph, NULL);
+  impl.Trim();
+}
+}
+}
+}  // namespace v8::internal::compiler
diff --git a/src/compiler/control-reducer.h b/src/compiler/control-reducer.h
new file mode 100644 (file)
index 0000000..d69d966
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2014 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_CONTROL_REDUCER_H_
+#define V8_COMPILER_CONTROL_REDUCER_H_
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+class JSGraph;
+class CommonOperatorBuilder;
+
+class ControlReducer {
+ public:
+  // Perform branch folding and dead code elimination on the graph.
+  static void ReduceGraph(JSGraph* graph, CommonOperatorBuilder* builder);
+
+  // Trim nodes in the graph that are not reachable from end.
+  static void TrimGraph(JSGraph* graph);
+};
+}
+}
+}  // namespace v8::internal::compiler
+
+#endif  // V8_COMPILER_CONTROL_REDUCER_H_
index 354aa9d..6459678 100644 (file)
@@ -11,6 +11,7 @@
 #include "src/compiler/basic-block-instrumentor.h"
 #include "src/compiler/change-lowering.h"
 #include "src/compiler/code-generator.h"
+#include "src/compiler/control-reducer.h"
 #include "src/compiler/graph-replay.h"
 #include "src/compiler/graph-visualizer.h"
 #include "src/compiler/instruction.h"
@@ -351,6 +352,16 @@ Handle<Code> Pipeline::GenerateCode() {
       // TODO(jarin, rossberg): Remove UNTYPED once machine typing works.
       VerifyAndPrintGraph(&graph, "Lowered changes", true);
     }
+
+    {
+      SourcePositionTable::Scope pos(&source_positions,
+                                     SourcePosition::Unknown());
+      PhaseStats control_reducer_stats(info(), PhaseStats::CREATE_GRAPH,
+                                       "control reduction");
+      ControlReducer::ReduceGraph(&jsgraph, &common);
+
+      VerifyAndPrintGraph(&graph, "Control reduced");
+    }
   }
 
   {
index 003a643..da805f6 100644 (file)
@@ -57,6 +57,7 @@
         'compiler/test-branch-combine.cc',
         'compiler/test-changes-lowering.cc',
         'compiler/test-codegen-deopt.cc',
+        'compiler/test-control-reducer.cc',
         'compiler/test-gap-resolver.cc',
         'compiler/test-graph-reducer.cc',
         'compiler/test-instruction.cc',
index b61be30..ff8eee7 100644 (file)
   'test-debug/DebugEventContext': [PASS, NO_VARIANTS],
   'test-debug/DebugBreakInline': [PASS, NO_VARIANTS],
 
+  # Scheduling and verifying of empty for loops is broken.
+  'test-run-jsbranches/EmptyFor': [SKIP],
+
   ############################################################################
   # Slow tests.
   'test-api/Threading1': [PASS, ['mode == debug', SLOW]],
diff --git a/test/cctest/compiler/test-control-reducer.cc b/test/cctest/compiler/test-control-reducer.cc
new file mode 100644 (file)
index 0000000..91b0138
--- /dev/null
@@ -0,0 +1,242 @@
+// Copyright 2014 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/v8.h"
+#include "test/cctest/cctest.h"
+
+#include "src/compiler/common-operator.h"
+#include "src/compiler/control-reducer.h"
+#include "src/compiler/graph-inl.h"
+#include "src/compiler/js-graph.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+class CTrimTester : HandleAndZoneScope {
+ public:
+  CTrimTester()
+      : isolate(main_isolate()),
+        common(main_zone()),
+        graph(main_zone()),
+        jsgraph(&graph, &common, NULL, NULL),
+        start(graph.NewNode(common.Start(1))),
+        p0(graph.NewNode(common.Parameter(0), start)),
+        one(jsgraph.OneConstant()),
+        half(jsgraph.Constant(0.5)) {
+    graph.SetEnd(start);
+    graph.SetStart(start);
+  }
+
+  Isolate* isolate;
+  CommonOperatorBuilder common;
+  Graph graph;
+  JSGraph jsgraph;
+  Node* start;
+  Node* p0;
+  Node* one;
+  Node* half;
+
+  void Trim() { ControlReducer::TrimGraph(&jsgraph); }
+};
+
+
+bool IsUsedBy(Node* a, Node* b) {
+  for (UseIter i = a->uses().begin(); i != a->uses().end(); ++i) {
+    if (b == *i) return true;
+  }
+  return false;
+}
+
+
+TEST(Trim1_live) {
+  CTrimTester T;
+  CHECK(IsUsedBy(T.start, T.p0));
+  T.graph.SetEnd(T.p0);
+  T.Trim();
+  CHECK(IsUsedBy(T.start, T.p0));
+  CHECK_EQ(T.start, T.p0->InputAt(0));
+}
+
+
+TEST(Trim1_dead) {
+  CTrimTester T;
+  CHECK(IsUsedBy(T.start, T.p0));
+  T.Trim();
+  CHECK(!IsUsedBy(T.start, T.p0));
+  CHECK_EQ(NULL, T.p0->InputAt(0));
+}
+
+
+TEST(Trim2_live) {
+  CTrimTester T;
+  Node* phi =
+      T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), T.one, T.half, T.start);
+  CHECK(IsUsedBy(T.one, phi));
+  CHECK(IsUsedBy(T.half, phi));
+  CHECK(IsUsedBy(T.start, phi));
+  T.graph.SetEnd(phi);
+  T.Trim();
+  CHECK(IsUsedBy(T.one, phi));
+  CHECK(IsUsedBy(T.half, phi));
+  CHECK(IsUsedBy(T.start, phi));
+  CHECK_EQ(T.one, phi->InputAt(0));
+  CHECK_EQ(T.half, phi->InputAt(1));
+  CHECK_EQ(T.start, phi->InputAt(2));
+}
+
+
+TEST(Trim2_dead) {
+  CTrimTester T;
+  Node* phi =
+      T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), T.one, T.half, T.start);
+  CHECK(IsUsedBy(T.one, phi));
+  CHECK(IsUsedBy(T.half, phi));
+  CHECK(IsUsedBy(T.start, phi));
+  T.Trim();
+  CHECK(!IsUsedBy(T.one, phi));
+  CHECK(!IsUsedBy(T.half, phi));
+  CHECK(!IsUsedBy(T.start, phi));
+  CHECK_EQ(NULL, phi->InputAt(0));
+  CHECK_EQ(NULL, phi->InputAt(1));
+  CHECK_EQ(NULL, phi->InputAt(2));
+}
+
+
+TEST(Trim_chain1) {
+  CTrimTester T;
+  const int kDepth = 15;
+  Node* live[kDepth];
+  Node* dead[kDepth];
+  Node* end = T.start;
+  for (int i = 0; i < kDepth; i++) {
+    live[i] = end = T.graph.NewNode(T.common.Merge(1), end);
+    dead[i] = T.graph.NewNode(T.common.Merge(1), end);
+  }
+  // end         -> live[last] ->  live[last-1] -> ... -> start
+  //     dead[last] ^ dead[last-1] ^ ...                  ^
+  T.graph.SetEnd(end);
+  T.Trim();
+  for (int i = 0; i < kDepth; i++) {
+    CHECK(!IsUsedBy(live[i], dead[i]));
+    CHECK_EQ(NULL, dead[i]->InputAt(0));
+    CHECK_EQ(i == 0 ? T.start : live[i - 1], live[i]->InputAt(0));
+  }
+}
+
+
+TEST(Trim_chain2) {
+  CTrimTester T;
+  const int kDepth = 15;
+  Node* live[kDepth];
+  Node* dead[kDepth];
+  Node* l = T.start;
+  Node* d = T.start;
+  for (int i = 0; i < kDepth; i++) {
+    live[i] = l = T.graph.NewNode(T.common.Merge(1), l);
+    dead[i] = d = T.graph.NewNode(T.common.Merge(1), d);
+  }
+  // end -> live[last] -> live[last-1] -> ... -> start
+  //        dead[last] -> dead[last-1] -> ... -> start
+  T.graph.SetEnd(l);
+  T.Trim();
+  CHECK(!IsUsedBy(T.start, dead[0]));
+  for (int i = 0; i < kDepth; i++) {
+    CHECK_EQ(i == 0 ? NULL : dead[i - 1], dead[i]->InputAt(0));
+    CHECK_EQ(i == 0 ? T.start : live[i - 1], live[i]->InputAt(0));
+  }
+}
+
+
+TEST(Trim_cycle1) {
+  CTrimTester T;
+  Node* loop = T.graph.NewNode(T.common.Loop(1), T.start, T.start);
+  loop->ReplaceInput(1, loop);
+  Node* end = T.graph.NewNode(T.common.End(), loop);
+  T.graph.SetEnd(end);
+
+  CHECK(IsUsedBy(T.start, loop));
+  CHECK(IsUsedBy(loop, end));
+  CHECK(IsUsedBy(loop, loop));
+
+  T.Trim();
+
+  // nothing should have happened to the loop itself.
+  CHECK(IsUsedBy(T.start, loop));
+  CHECK(IsUsedBy(loop, end));
+  CHECK(IsUsedBy(loop, loop));
+  CHECK_EQ(T.start, loop->InputAt(0));
+  CHECK_EQ(loop, loop->InputAt(1));
+  CHECK_EQ(loop, end->InputAt(0));
+}
+
+
+TEST(Trim_cycle2) {
+  CTrimTester T;
+  Node* loop = T.graph.NewNode(T.common.Loop(2), T.start, T.start);
+  loop->ReplaceInput(1, loop);
+  Node* end = T.graph.NewNode(T.common.End(), loop);
+  Node* phi =
+      T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), T.one, T.half, loop);
+  T.graph.SetEnd(end);
+
+  CHECK(IsUsedBy(T.start, loop));
+  CHECK(IsUsedBy(loop, end));
+  CHECK(IsUsedBy(loop, loop));
+  CHECK(IsUsedBy(loop, phi));
+  CHECK(IsUsedBy(T.one, phi));
+  CHECK(IsUsedBy(T.half, phi));
+
+  T.Trim();
+
+  // nothing should have happened to the loop itself.
+  CHECK(IsUsedBy(T.start, loop));
+  CHECK(IsUsedBy(loop, end));
+  CHECK(IsUsedBy(loop, loop));
+  CHECK_EQ(T.start, loop->InputAt(0));
+  CHECK_EQ(loop, loop->InputAt(1));
+  CHECK_EQ(loop, end->InputAt(0));
+
+  // phi should have been trimmed away.
+  CHECK(!IsUsedBy(loop, phi));
+  CHECK(!IsUsedBy(T.one, phi));
+  CHECK(!IsUsedBy(T.half, phi));
+  CHECK_EQ(NULL, phi->InputAt(0));
+  CHECK_EQ(NULL, phi->InputAt(1));
+  CHECK_EQ(NULL, phi->InputAt(2));
+}
+
+
+void CheckTrimConstant(CTrimTester* T, Node* k) {
+  Node* phi = T->graph.NewNode(T->common.Phi(kMachInt32, 1), k, T->start);
+  CHECK(IsUsedBy(k, phi));
+  T->Trim();
+  CHECK(!IsUsedBy(k, phi));
+  CHECK_EQ(NULL, phi->InputAt(0));
+  CHECK_EQ(NULL, phi->InputAt(1));
+}
+
+
+TEST(Trim_constants) {
+  CTrimTester T;
+  int32_t int32_constants[] = {
+      0, -1,  -2,  2,  2,  3,  3,  4,  4,  5,  5,  4,  5,  6, 6, 7, 8, 7, 8, 9,
+      0, -11, -12, 12, 12, 13, 13, 14, 14, 15, 15, 14, 15, 6, 6, 7, 8, 7, 8, 9};
+
+  for (size_t i = 0; i < arraysize(int32_constants); i++) {
+    CheckTrimConstant(&T, T.jsgraph.Int32Constant(int32_constants[i]));
+    CheckTrimConstant(&T, T.jsgraph.Float64Constant(int32_constants[i]));
+    CheckTrimConstant(&T, T.jsgraph.Constant(int32_constants[i]));
+  }
+
+  Node* other_constants[] = {
+      T.jsgraph.UndefinedConstant(), T.jsgraph.TheHoleConstant(),
+      T.jsgraph.TrueConstant(),      T.jsgraph.FalseConstant(),
+      T.jsgraph.NullConstant(),      T.jsgraph.ZeroConstant(),
+      T.jsgraph.OneConstant(),       T.jsgraph.NaNConstant(),
+      T.jsgraph.Constant(21),        T.jsgraph.Constant(22.2)};
+
+  for (size_t i = 0; i < arraysize(other_constants); i++) {
+    CheckTrimConstant(&T, other_constants[i]);
+  }
+}
index df2fcdc..7a4a0b3 100644 (file)
@@ -280,3 +280,78 @@ TEST(NestedForConditional) {
   T.CheckCall(T.Val(2), T.Val(2), T.false_value());
   T.CheckCall(T.undefined(), T.Val(1), T.null());
 }
+
+
+TEST(IfTrue) {
+  FunctionTester T("(function(a,b) { if (true) return a; return b; })");
+
+  T.CheckCall(T.Val(55), T.Val(55), T.Val(11));
+  T.CheckCall(T.Val(666), T.Val(666), T.Val(-444));
+}
+
+
+TEST(TernaryTrue) {
+  FunctionTester T("(function(a,b) { return true ? a : b; })");
+
+  T.CheckCall(T.Val(77), T.Val(77), T.Val(11));
+  T.CheckCall(T.Val(111), T.Val(111), T.Val(-444));
+}
+
+
+TEST(IfFalse) {
+  FunctionTester T("(function(a,b) { if (false) return a; return b; })");
+
+  T.CheckCall(T.Val(11), T.Val(22), T.Val(11));
+  T.CheckCall(T.Val(-555), T.Val(333), T.Val(-555));
+}
+
+
+TEST(TernaryFalse) {
+  FunctionTester T("(function(a,b) { return false ? a : b; })");
+
+  T.CheckCall(T.Val(99), T.Val(33), T.Val(99));
+  T.CheckCall(T.Val(-99), T.Val(-33), T.Val(-99));
+}
+
+
+TEST(WhileTrue) {
+  FunctionTester T("(function(a,b) { while (true) return a; return b; })");
+
+  T.CheckCall(T.Val(551), T.Val(551), T.Val(111));
+  T.CheckCall(T.Val(661), T.Val(661), T.Val(-444));
+}
+
+
+TEST(WhileFalse) {
+  FunctionTester T("(function(a,b) { while (false) return a; return b; })");
+
+  T.CheckCall(T.Val(115), T.Val(551), T.Val(115));
+  T.CheckCall(T.Val(-445), T.Val(661), T.Val(-445));
+}
+
+
+TEST(DoWhileTrue) {
+  FunctionTester T(
+      "(function(a,b) { do { return a; } while (true); return b; })");
+
+  T.CheckCall(T.Val(7551), T.Val(7551), T.Val(7111));
+  T.CheckCall(T.Val(7661), T.Val(7661), T.Val(-7444));
+}
+
+
+TEST(DoWhileFalse) {
+  FunctionTester T(
+      "(function(a,b) { do { "
+      "; } while (false); return b; })");
+
+  T.CheckCall(T.Val(8115), T.Val(8551), T.Val(8115));
+  T.CheckCall(T.Val(-8445), T.Val(8661), T.Val(-8445));
+}
+
+
+TEST(EmptyFor) {
+  FunctionTester T("(function(a,b) { if (a) for(;;) ; return b; })");
+
+  T.CheckCall(T.Val(8126.1), T.Val(0.0), T.Val(8126.1));
+  T.CheckCall(T.Val(1123.1), T.Val(0.0), T.Val(1123.1));
+}
diff --git a/test/mjsunit/asm/do-while-false.js b/test/mjsunit/asm/do-while-false.js
new file mode 100644 (file)
index 0000000..ba906a0
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright 2014 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.
+
+function Module() {
+  "use asm";
+
+  function d0() {
+    do { } while(false);
+    return 110;
+  }
+
+  function d1() {
+    do { return 111; } while(false);
+    return 112;
+  }
+
+  function d2() {
+    do { break; } while(false);
+    return 113;
+  }
+
+  function d3(a) {
+    a = a | 0;
+    do { if (a) return 114; } while(false);
+    return 115;
+  }
+
+  function d4(a) {
+    a = a | 0;
+    do { if (a) return 116; else break; } while(false);
+    return 117;
+  }
+
+  function d5(a) {
+    a = a | 0;
+    do { if (a) return 118; } while(false);
+    return 119;
+  }
+
+  function d6(a) {
+    a = a | 0;
+    do {
+      if (a == 0) return 120;
+      if (a == 1) break;
+      if (a == 2) return 122;
+      if (a == 3) continue;
+      if (a == 4) return 124;
+    } while(false);
+    return 125;
+  }
+
+  return {d0: d0, d1: d1, d2: d2, d3: d3, d4: d4, d5: d5, d6: d6};
+}
+
+var m = Module();
+
+assertEquals(110, m.d0());
+
+assertEquals(111, m.d1());
+
+assertEquals(113, m.d2());
+
+assertEquals(114, m.d3(1));
+assertEquals(115, m.d3(0));
+
+assertEquals(116, m.d4(1));
+assertEquals(117, m.d4(0));
+
+assertEquals(118, m.d5(1));
+assertEquals(119, m.d5(0));
+
+assertEquals(120, m.d6(0));
+assertEquals(125, m.d6(1));
+assertEquals(122, m.d6(2));
+assertEquals(125, m.d6(3));
+assertEquals(124, m.d6(4));
+assertEquals(125, m.d6(5));
diff --git a/test/mjsunit/asm/if-folding.js b/test/mjsunit/asm/if-folding.js
new file mode 100644 (file)
index 0000000..80070ee
--- /dev/null
@@ -0,0 +1,100 @@
+// Copyright 2014 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.
+
+function Module() {
+  "use asm";
+
+  function if0() {
+    if (0) return 11;
+    return 12;
+  }
+
+  function if1() {
+    if (1) return 13;
+    return 14;
+  }
+
+  function if2() {
+    if (0) return 15;
+    else return 16;
+  }
+
+  function if3() {
+    if (1) return 17;
+    else return 18;
+  }
+
+  function if4() {
+    return 1 ? 19 : 20;
+  }
+
+  function if5() {
+    return 0 ? 21 : 22;
+  }
+
+  function if6() {
+    var x = 0 ? 23 : 24;
+    return x;
+  }
+
+  function if7() {
+    if (0) { var x = 0 ? 25 : 26; }
+    else { var x = 0 ? 27 : 28; }
+    return x;
+  }
+
+  function if8() {
+    if (0) {
+      if (0) { var x = 0 ? 29 : 30; }
+      else { var x = 0 ? 31 : 32; }
+    } else {
+      if (0) { var x = 0 ? 33 : 34; }
+      else { var x = 0 ? 35 : 36; }
+    }
+    return x;
+  }
+
+  return {if0: if0, if1: if1, if2: if2, if3: if3, if4: if4, if5: if5, if6: if6, if7: if7, if8: if8 };
+}
+
+var m = Module();
+assertEquals(12, m.if0());
+assertEquals(13, m.if1());
+assertEquals(16, m.if2());
+assertEquals(17, m.if3());
+assertEquals(19, m.if4());
+assertEquals(22, m.if5());
+assertEquals(24, m.if6());
+assertEquals(28, m.if7());
+assertEquals(36, m.if8());
+
+
+function Spec(a,b,c) {
+  "use asm";
+
+  var xx = a | 0;
+  var yy = b | 0;
+  var zz = c | 0;
+
+  function f() {
+    if (xx) {
+      if (yy) { var x = zz ? 29 : 30; }
+      else { var x = zz ? 31 : 32; }
+    } else {
+      if (yy) { var x = zz ? 33 : 34; }
+      else { var x = zz ? 35 : 36; }
+    }
+    return x;
+  }
+  return {f: f};
+}
+
+assertEquals(36, Spec(0,0,0).f());
+assertEquals(35, Spec(0,0,1).f());
+assertEquals(34, Spec(0,1,0).f());
+assertEquals(33, Spec(0,1,1).f());
+assertEquals(32, Spec(1,0,0).f());
+assertEquals(31, Spec(1,0,1).f());
+assertEquals(30, Spec(1,1,0).f());
+assertEquals(29, Spec(1,1,1).f());
diff --git a/test/mjsunit/asm/if-reduction.js b/test/mjsunit/asm/if-reduction.js
new file mode 100644 (file)
index 0000000..b0dcc13
--- /dev/null
@@ -0,0 +1,111 @@
+// Copyright 2014 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.
+
+function Module() {
+  "use asm";
+
+  function if0() {
+    var x = 0 ? 11 : 12;
+    return (x == 11) | 0;
+  }
+
+  function if1() {
+    var x = 1 ? 13 : 14;
+    return (x == 13) | 0;
+  }
+
+  function if2() {
+    var x = 0 ? 15 : 16;
+    return (x != 15) | 0;
+  }
+
+  function if3() {
+    var x = 1 ? 17 : 18;
+    return (x != 17) | 0;
+  }
+
+  function if4() {
+    var x = 0 ? 19 : 20;
+    var y = (x == 19) ? 21 : 22;
+    return y;
+  }
+
+  function if5() {
+    var x = 1 ? 23 : 24;
+    var y = (x == 23) ? 25 : 26;
+    return y;
+  }
+
+  function if6() {
+    var x = 0 ? 27 : 28;
+    var y = (x == 27) ? 29 : 30;
+    var z = (y == 29) ? 31 : 32;
+    return z;
+  }
+
+  function if7() {
+    var x = 1 ? 33 : 34;
+    var y = (x == 33) ? 35 : 36;
+    var z = (y == 35) ? 37 : 38;
+    var w = (z == 37) ? 39 : 40;
+    return w;
+  }
+
+  function if8() {
+    if (0) {
+      var x = 0 ? 43 : 44;
+      var y = (x == 43) ? 45 : 46;
+      var z = (y == 45) ? 47 : 48;
+      var w = (z == 47) ? 49 : 50;
+    } else {
+      var x = 1 ? 53 : 54;
+      var y = (x == 53) ? 55 : 56;
+      var z = (y == 55) ? 57 : 58;
+      var w = (z == 57) ? 59 : 60;
+    }
+    return w;
+  }
+
+  return {if0: if0, if1: if1, if2: if2, if3: if3, if4: if4, if5: if5, if6: if6, if7: if7, if8: if8 };
+}
+
+var m = Module();
+assertEquals(0, m.if0());
+assertEquals(1, m.if1());
+assertEquals(1, m.if2());
+assertEquals(0, m.if3());
+assertEquals(22, m.if4());
+assertEquals(25, m.if5());
+assertEquals(32, m.if6());
+assertEquals(39, m.if7());
+assertEquals(59, m.if8());
+
+
+function Spec(a,b) {
+  "use asm";
+
+  var xx = a | 0;
+  var yy = b | 0;
+
+  function f() {
+    if (xx) {
+      var x = yy ? 43 : 44;
+      var y = (x == 43) ? 45 : 46;
+      var z = (y == 45) ? 47 : 48;
+      var w = (z == 47) ? 49 : 50;
+    } else {
+      var x = yy ? 53 : 54;
+      var y = (x == 53) ? 55 : 56;
+      var z = (y == 55) ? 57 : 58;
+      var w = (z == 57) ? 59 : 60;
+    }
+    return w;
+  }
+  return {f: f};
+}
+
+assertEquals(60, Spec(0,0).f());
+assertEquals(59, Spec(0,1).f());
+assertEquals(50, Spec(1,0).f());
+assertEquals(49, Spec(1,1).f());
diff --git a/test/mjsunit/asm/infinite-loops-taken.js b/test/mjsunit/asm/infinite-loops-taken.js
new file mode 100644 (file)
index 0000000..d136c62
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright 2014 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.
+
+var error = "error";
+function counter(x) {
+  return (function() { if (x-- == 0) throw error;});
+}
+
+function Module() {
+  "use asm";
+
+  function w0(f) {
+    while (1) f();
+    return 108;
+  }
+
+  function w1(f) {
+    if (1) while (1) f();
+    return 109;
+  }
+
+  function w2(f) {
+    if (1) while (1) f();
+    else while (1) f();
+    return 110;
+  }
+
+  function w3(f) {
+    if (0) while (1) f();
+    return 111;
+  }
+
+  return { w0: w0, w1: w1, w2: w2, w3: w3 };
+}
+
+var m = Module();
+assertThrows(function() { m.w0(counter(5)) }, error);
+assertThrows(function() { m.w1(counter(5)) }, error);
+assertThrows(function() { m.w2(counter(5)) }, error);
+assertEquals(111, m.w3(counter(5)));
diff --git a/test/mjsunit/asm/infinite-loops.js b/test/mjsunit/asm/infinite-loops.js
new file mode 100644 (file)
index 0000000..03f4f6b
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright 2014 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.
+
+function Module() {
+  "use asm";
+
+  function w0(a) {
+    a = a | 0;
+    if (a) while (1);
+    return 42;
+  }
+
+  function w1(a) {
+    a = a | 0;
+    while (1) return 42;
+    return 106;
+  }
+
+  function d0(a) {
+    a = a | 0;
+    if (a) do ; while(1);
+    return 42;
+  }
+
+  function d1(a) {
+    a = a | 0;
+    do return 42; while(1);
+    return 107;
+  }
+
+  function f0(a) {
+    a = a | 0;
+    if (a) for (;;) ;
+    return 42;
+  }
+
+  function f1(a) {
+    a = a | 0;
+    for(;;) return 42;
+    return 108;
+  }
+
+  return { w0: w0, w1: w1, d0: d0, d1: d1, f0: f0, f1: f1 };
+}
+
+var m = Module();
+assertEquals(42, m.w0(0));
+assertEquals(42, m.w1(0));
+assertEquals(42, m.d0(0));
+assertEquals(42, m.d1(0));
+assertEquals(42, m.f0(0));
+assertEquals(42, m.f1(0));
index 3efd3b5..9e317c0 100644 (file)
   'regress/regress-crbug-259300': [PASS, NO_VARIANTS],
   'regress/regress-frame-details-null-receiver': [PASS, NO_VARIANTS],
 
+  # Infinite loops of the form "for(;;) ;" don't schedule or crash in verifier.
+  'asm/infinite-loops': [PASS, NO_VARIANTS],
+
   ##############################################################################
   # Too slow in debug mode with --stress-opt mode.
   'compiler/regress-stacktrace-methods': [PASS, ['mode == debug', SKIP]],
index cfec1b1..196df4f 100644 (file)
         '../../src/compiler/common-operator.h',
         '../../src/compiler/control-builders.cc',
         '../../src/compiler/control-builders.h',
+        '../../src/compiler/control-reducer.cc',
+        '../../src/compiler/control-reducer.h',
         '../../src/compiler/frame.h',
         '../../src/compiler/gap-resolver.cc',
         '../../src/compiler/gap-resolver.h',