Update V8 to version 4.1.0.21
This is a cherry-pick of all commits up to and including the
4.1.0.21 cherry-pick in Chromium.
Original commit message:
Version 4.1.0.21 (cherry-pick)
Merged 206e9136bde0f2b5ae8cb77afbb1e7833e5bd412
Unlink pages from the space page list after evacuation.
BUG=430201
LOG=N
R=jkummerow@chromium.org
Review URL: https://codereview.chromium.org/953813002
Cr-Commit-Position: refs/branch-heads/4.1@{#22}
Cr-Branched-From: 2e08d2a7aa9d65d269d8c57aba82eb38a8cb0a18-refs/heads/candidates@{#25353}
---
FPIIM-449
Change-Id: I8c23c7bbb70772b4858fe8a47b64fa97ee0d1f8c
diff --git a/test/cctest/compiler/call-tester.h b/test/cctest/compiler/call-tester.h
index e864160..cad171e 100644
--- a/test/cctest/compiler/call-tester.h
+++ b/test/cctest/compiler/call-tester.h
@@ -207,7 +207,43 @@
Simulator::CallArgument::End()};
return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f), args));
}
-#elif USE_SIMULATOR && V8_TARGET_ARCH_ARM
+#elif USE_SIMULATOR && V8_TARGET_ARCH_MIPS64
+ uintptr_t CallSimulator(byte* f, int64_t p1 = 0, int64_t p2 = 0,
+ int64_t p3 = 0, int64_t p4 = 0) {
+ Simulator* simulator = Simulator::current(isolate_);
+ return static_cast<uintptr_t>(simulator->Call(f, 4, p1, p2, p3, p4));
+ }
+
+ template <typename R, typename F>
+ R DoCall(F* f) {
+ return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f)));
+ }
+ template <typename R, typename F, typename P1>
+ R DoCall(F* f, P1 p1) {
+ return ReturnValueTraits<R>::Cast(
+ CallSimulator(FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1)));
+ }
+ template <typename R, typename F, typename P1, typename P2>
+ R DoCall(F* f, P1 p1, P2 p2) {
+ return ReturnValueTraits<R>::Cast(
+ CallSimulator(FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1),
+ ParameterTraits<P2>::Cast(p2)));
+ }
+ template <typename R, typename F, typename P1, typename P2, typename P3>
+ R DoCall(F* f, P1 p1, P2 p2, P3 p3) {
+ return ReturnValueTraits<R>::Cast(CallSimulator(
+ FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1),
+ ParameterTraits<P2>::Cast(p2), ParameterTraits<P3>::Cast(p3)));
+ }
+ template <typename R, typename F, typename P1, typename P2, typename P3,
+ typename P4>
+ R DoCall(F* f, P1 p1, P2 p2, P3 p3, P4 p4) {
+ return ReturnValueTraits<R>::Cast(CallSimulator(
+ FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1),
+ ParameterTraits<P2>::Cast(p2), ParameterTraits<P3>::Cast(p3),
+ ParameterTraits<P4>::Cast(p4)));
+ }
+#elif USE_SIMULATOR && (V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS)
uintptr_t CallSimulator(byte* f, int32_t p1 = 0, int32_t p2 = 0,
int32_t p3 = 0, int32_t p4 = 0) {
Simulator* simulator = Simulator::current(isolate_);
diff --git a/test/cctest/compiler/codegen-tester.cc b/test/cctest/compiler/codegen-tester.cc
index b1874f5..5311001 100644
--- a/test/cctest/compiler/codegen-tester.cc
+++ b/test/cctest/compiler/codegen-tester.cc
@@ -14,7 +14,6 @@
TEST(CompareWrapper) {
// Who tests the testers?
// If CompareWrapper is broken, then test expectations will be broken.
- RawMachineAssemblerTester<int32_t> m;
CompareWrapper wWord32Equal(IrOpcode::kWord32Equal);
CompareWrapper wInt32LessThan(IrOpcode::kInt32LessThan);
CompareWrapper wInt32LessThanOrEqual(IrOpcode::kInt32LessThanOrEqual);
@@ -477,10 +476,10 @@
TEST(RunHeapNumberConstant) {
- RawMachineAssemblerTester<Object*> m;
- Handle<Object> number = m.isolate()->factory()->NewHeapNumber(100.5);
+ RawMachineAssemblerTester<HeapObject*> m;
+ Handle<HeapObject> number = m.isolate()->factory()->NewHeapNumber(100.5);
m.Return(m.HeapConstant(number));
- Object* result = m.Call();
+ HeapObject* result = m.Call();
CHECK_EQ(result, *number);
}
diff --git a/test/cctest/compiler/codegen-tester.h b/test/cctest/compiler/codegen-tester.h
index 6aa5bae..283d533 100644
--- a/test/cctest/compiler/codegen-tester.h
+++ b/test/cctest/compiler/codegen-tester.h
@@ -7,6 +7,7 @@
#include "src/v8.h"
+#include "src/compiler/instruction-selector.h"
#include "src/compiler/pipeline.h"
#include "src/compiler/raw-machine-assembler.h"
#include "src/simulator.h"
@@ -23,7 +24,9 @@
public:
MachineAssemblerTester(MachineType return_type, MachineType p0,
MachineType p1, MachineType p2, MachineType p3,
- MachineType p4)
+ MachineType p4,
+ MachineOperatorBuilder::Flags flags =
+ MachineOperatorBuilder::Flag::kNoFlags)
: HandleAndZoneScope(),
CallHelper(
main_isolate(),
@@ -31,7 +34,7 @@
MachineAssembler(
new (main_zone()) Graph(main_zone()),
MakeMachineSignature(main_zone(), return_type, p0, p1, p2, p3, p4),
- kMachPtr) {}
+ kMachPtr, flags) {}
Node* LoadFromPointer(void* address, MachineType rep, int32_t offset = 0) {
return this->Load(rep, this->PointerConstant(address),
@@ -65,10 +68,8 @@
Schedule* schedule = this->Export();
CallDescriptor* call_descriptor = this->call_descriptor();
Graph* graph = this->graph();
- CompilationInfo info(graph->zone()->isolate(), graph->zone());
- Linkage linkage(&info, call_descriptor);
- Pipeline pipeline(&info);
- code_ = pipeline.GenerateCodeForMachineGraph(&linkage, graph, schedule);
+ code_ =
+ Pipeline::GenerateCodeForTesting(call_descriptor, graph, schedule);
}
return this->code_.ToHandleChecked()->entry();
}
@@ -89,8 +90,8 @@
MachineType p3 = kMachNone,
MachineType p4 = kMachNone)
: MachineAssemblerTester<RawMachineAssembler>(
- ReturnValueTraits<ReturnType>::Representation(), p0, p1, p2, p3,
- p4) {}
+ ReturnValueTraits<ReturnType>::Representation(), p0, p1, p2, p3, p4,
+ InstructionSelector::SupportedMachineOperatorFlags()) {}
template <typename Ci, typename Fn>
void Run(const Ci& ci, const Fn& fn) {
diff --git a/test/cctest/compiler/function-tester.h b/test/cctest/compiler/function-tester.h
index c869f00..7e16eea 100644
--- a/test/cctest/compiler/function-tester.h
+++ b/test/cctest/compiler/function-tester.h
@@ -8,7 +8,9 @@
#include "src/v8.h"
#include "test/cctest/cctest.h"
+#include "src/ast-numbering.h"
#include "src/compiler.h"
+#include "src/compiler/linkage.h"
#include "src/compiler/pipeline.h"
#include "src/execution.h"
#include "src/full-codegen.h"
@@ -37,52 +39,16 @@
CHECK_EQ(0, flags_ & ~supported_flags);
}
+ explicit FunctionTester(Graph* graph)
+ : isolate(main_isolate()),
+ function(NewFunction("(function(a,b){})")),
+ flags_(0) {
+ CompileGraph(graph);
+ }
+
Isolate* isolate;
Handle<JSFunction> function;
- Handle<JSFunction> Compile(Handle<JSFunction> function) {
-#if V8_TURBOFAN_TARGET
- CompilationInfoWithZone info(function);
-
- CHECK(Parser::Parse(&info));
- info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
- if (flags_ & CompilationInfo::kContextSpecializing) {
- info.MarkAsContextSpecializing();
- }
- if (flags_ & CompilationInfo::kInliningEnabled) {
- info.MarkAsInliningEnabled();
- }
- if (flags_ & CompilationInfo::kTypingEnabled) {
- info.MarkAsTypingEnabled();
- }
- CHECK(Rewriter::Rewrite(&info));
- CHECK(Scope::Analyze(&info));
- CHECK(Compiler::EnsureDeoptimizationSupport(&info));
-
- Pipeline pipeline(&info);
- Handle<Code> code = pipeline.GenerateCode();
- if (FLAG_turbo_deoptimization) {
- info.context()->native_context()->AddOptimizedCode(*code);
- }
-
- CHECK(!code.is_null());
- function->ReplaceCode(*code);
-#elif USE_CRANKSHAFT
- Handle<Code> unoptimized = Handle<Code>(function->code());
- Handle<Code> code = Compiler::GetOptimizedCode(function, unoptimized,
- Compiler::NOT_CONCURRENT);
- CHECK(!code.is_null());
-#if ENABLE_DISASSEMBLER
- if (FLAG_print_opt_code) {
- CodeTracer::Scope tracing_scope(isolate->GetCodeTracer());
- code->Disassemble("test code", tracing_scope.file());
- }
-#endif
- function->ReplaceCode(*code);
-#endif
- return function;
- }
-
MaybeHandle<Object> Call(Handle<Object> a, Handle<Object> b) {
Handle<Object> args[] = {a, b};
return Execution::Call(isolate, function, undefined(), 2, args, false);
@@ -183,8 +149,78 @@
Handle<Object> false_value() { return isolate->factory()->false_value(); }
+ Handle<JSFunction> Compile(Handle<JSFunction> function) {
+// TODO(titzer): make this method private.
+#if V8_TURBOFAN_TARGET
+ CompilationInfoWithZone info(function);
+
+ CHECK(Parser::Parse(&info));
+ info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
+ if (flags_ & CompilationInfo::kContextSpecializing) {
+ info.MarkAsContextSpecializing();
+ }
+ if (flags_ & CompilationInfo::kInliningEnabled) {
+ info.MarkAsInliningEnabled();
+ }
+ if (flags_ & CompilationInfo::kTypingEnabled) {
+ info.MarkAsTypingEnabled();
+ }
+ CHECK(Compiler::Analyze(&info));
+ CHECK(Compiler::EnsureDeoptimizationSupport(&info));
+
+ Pipeline pipeline(&info);
+ Handle<Code> code = pipeline.GenerateCode();
+ if (FLAG_turbo_deoptimization) {
+ info.context()->native_context()->AddOptimizedCode(*code);
+ }
+
+ CHECK(!code.is_null());
+ function->ReplaceCode(*code);
+#elif USE_CRANKSHAFT
+ Handle<Code> unoptimized = Handle<Code>(function->code());
+ Handle<Code> code = Compiler::GetOptimizedCode(function, unoptimized,
+ Compiler::NOT_CONCURRENT);
+ CHECK(!code.is_null());
+#if ENABLE_DISASSEMBLER
+ if (FLAG_print_opt_code) {
+ CodeTracer::Scope tracing_scope(isolate->GetCodeTracer());
+ code->Disassemble("test code", tracing_scope.file());
+ }
+#endif
+ function->ReplaceCode(*code);
+#endif
+ return function;
+ }
+
+ static Handle<JSFunction> ForMachineGraph(Graph* graph) {
+ JSFunction* p = NULL;
+ { // because of the implicit handle scope of FunctionTester.
+ FunctionTester f(graph);
+ p = *f.function;
+ }
+ return Handle<JSFunction>(p); // allocated in outer handle scope.
+ }
+
private:
uint32_t flags_;
+
+ // Compile the given machine graph instead of the source of the function
+ // and replace the JSFunction's code with the result.
+ Handle<JSFunction> CompileGraph(Graph* graph) {
+ CHECK(Pipeline::SupportedTarget());
+ CompilationInfoWithZone info(function);
+
+ CHECK(Parser::Parse(&info));
+ info.SetOptimizing(BailoutId::None(),
+ Handle<Code>(function->shared()->code()));
+ CHECK(Compiler::Analyze(&info));
+ CHECK(Compiler::EnsureDeoptimizationSupport(&info));
+
+ Handle<Code> code = Pipeline::GenerateCodeForTesting(&info, graph);
+ CHECK(!code.is_null());
+ function->ReplaceCode(*code);
+ return function;
+ }
};
}
}
diff --git a/test/cctest/compiler/graph-builder-tester.cc b/test/cctest/compiler/graph-builder-tester.cc
index bfa8226..b0f470b 100644
--- a/test/cctest/compiler/graph-builder-tester.cc
+++ b/test/cctest/compiler/graph-builder-tester.cc
@@ -35,11 +35,9 @@
if (!Pipeline::SupportedBackend()) return NULL;
if (code_.is_null()) {
Zone* zone = graph_->zone();
- CompilationInfo info(zone->isolate(), zone);
- Linkage linkage(&info,
- Linkage::GetSimplifiedCDescriptor(zone, machine_sig_));
- Pipeline pipeline(&info);
- code_ = pipeline.GenerateCodeForMachineGraph(&linkage, graph_);
+ CallDescriptor* desc =
+ Linkage::GetSimplifiedCDescriptor(zone, machine_sig_);
+ code_ = Pipeline::GenerateCodeForTesting(desc, graph_);
}
return code_.ToHandleChecked()->entry();
}
diff --git a/test/cctest/compiler/graph-builder-tester.h b/test/cctest/compiler/graph-builder-tester.h
index df79250..772de4d 100644
--- a/test/cctest/compiler/graph-builder-tester.h
+++ b/test/cctest/compiler/graph-builder-tester.h
@@ -27,8 +27,8 @@
protected:
virtual Node* MakeNode(const Operator* op, int value_input_count,
- Node** value_inputs) FINAL {
- return graph()->NewNode(op, value_input_count, value_inputs);
+ Node** value_inputs, bool incomplete) FINAL {
+ return graph()->NewNode(op, value_input_count, value_inputs, incomplete);
}
};
@@ -61,6 +61,7 @@
explicit GraphAndBuilders(Zone* zone)
: main_graph_(new (zone) Graph(zone)),
main_common_(zone),
+ main_machine_(zone),
main_simplified_(zone) {}
protected:
diff --git a/test/cctest/compiler/simplified-graph-builder.cc b/test/cctest/compiler/simplified-graph-builder.cc
index c44d5ed..baa03fb 100644
--- a/test/cctest/compiler/simplified-graph-builder.cc
+++ b/test/cctest/compiler/simplified-graph-builder.cc
@@ -5,7 +5,6 @@
#include "test/cctest/compiler/simplified-graph-builder.h"
#include "src/compiler/operator-properties.h"
-#include "src/compiler/operator-properties-inl.h"
namespace v8 {
namespace internal {
@@ -45,20 +44,20 @@
Node* SimplifiedGraphBuilder::MakeNode(const Operator* op,
int value_input_count,
- Node** value_inputs) {
- DCHECK(op->InputCount() == value_input_count);
+ Node** value_inputs, bool incomplete) {
+ DCHECK(op->ValueInputCount() == value_input_count);
DCHECK(!OperatorProperties::HasContextInput(op));
DCHECK(!OperatorProperties::HasFrameStateInput(op));
- bool has_control = OperatorProperties::GetControlInputCount(op) == 1;
- bool has_effect = OperatorProperties::GetEffectInputCount(op) == 1;
+ bool has_control = op->ControlInputCount() == 1;
+ bool has_effect = op->EffectInputCount() == 1;
- DCHECK(OperatorProperties::GetControlInputCount(op) < 2);
- DCHECK(OperatorProperties::GetEffectInputCount(op) < 2);
+ DCHECK(op->ControlInputCount() < 2);
+ DCHECK(op->EffectInputCount() < 2);
Node* result = NULL;
if (!has_control && !has_effect) {
- result = graph()->NewNode(op, value_input_count, value_inputs);
+ result = graph()->NewNode(op, value_input_count, value_inputs, incomplete);
} else {
int input_count_with_deps = value_input_count;
if (has_control) ++input_count_with_deps;
@@ -72,14 +71,12 @@
if (has_control) {
*current_input++ = graph()->start();
}
- result = graph()->NewNode(op, input_count_with_deps, buffer);
+ result = graph()->NewNode(op, input_count_with_deps, buffer, incomplete);
if (has_effect) {
effect_ = result;
}
- if (OperatorProperties::HasControlOutput(result->op())) {
- // This graph builder does not support control flow.
- UNREACHABLE();
- }
+ // This graph builder does not support control flow.
+ CHECK_EQ(0, op->ControlOutputCount());
}
return result;
diff --git a/test/cctest/compiler/simplified-graph-builder.h b/test/cctest/compiler/simplified-graph-builder.h
index 1b637b7..537094a 100644
--- a/test/cctest/compiler/simplified-graph-builder.h
+++ b/test/cctest/compiler/simplified-graph-builder.h
@@ -45,8 +45,8 @@
Node* Int32Constant(int32_t value) {
return NewNode(common()->Int32Constant(value));
}
- Node* HeapConstant(Handle<Object> object) {
- Unique<Object> val = Unique<Object>::CreateUninitialized(object);
+ Node* HeapConstant(Handle<HeapObject> object) {
+ Unique<HeapObject> val = Unique<HeapObject>::CreateUninitialized(object);
return NewNode(common()->HeapConstant(val));
}
@@ -127,19 +127,17 @@
Node* StoreField(const FieldAccess& access, Node* object, Node* value) {
return NewNode(simplified()->StoreField(access), object, value);
}
- Node* LoadElement(const ElementAccess& access, Node* object, Node* index,
- Node* length) {
- return NewNode(simplified()->LoadElement(access), object, index, length);
+ Node* LoadElement(const ElementAccess& access, Node* object, Node* index) {
+ return NewNode(simplified()->LoadElement(access), object, index);
}
Node* StoreElement(const ElementAccess& access, Node* object, Node* index,
- Node* length, Node* value) {
- return NewNode(simplified()->StoreElement(access), object, index, length,
- value);
+ Node* value) {
+ return NewNode(simplified()->StoreElement(access), object, index, value);
}
protected:
virtual Node* MakeNode(const Operator* op, int value_input_count,
- Node** value_inputs) FINAL;
+ Node** value_inputs, bool incomplete) FINAL;
private:
Node* effect_;
diff --git a/test/cctest/compiler/test-basic-block-profiler.cc b/test/cctest/compiler/test-basic-block-profiler.cc
new file mode 100644
index 0000000..703fc17
--- /dev/null
+++ b/test/cctest/compiler/test-basic-block-profiler.cc
@@ -0,0 +1,114 @@
+// 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 "src/basic-block-profiler.h"
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/codegen-tester.h"
+
+#if V8_TURBOFAN_TARGET
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+typedef RawMachineAssembler::Label MLabel;
+
+class BasicBlockProfilerTest : public RawMachineAssemblerTester<int32_t> {
+ public:
+ BasicBlockProfilerTest() : RawMachineAssemblerTester<int32_t>(kMachInt32) {
+ FLAG_turbo_profiling = true;
+ }
+
+ void ResetCounts() { isolate()->basic_block_profiler()->ResetCounts(); }
+
+ void Expect(size_t size, uint32_t* expected) {
+ CHECK_NE(NULL, isolate()->basic_block_profiler());
+ const BasicBlockProfiler::DataList* l =
+ isolate()->basic_block_profiler()->data_list();
+ CHECK_NE(0, static_cast<int>(l->size()));
+ const BasicBlockProfiler::Data* data = l->back();
+ CHECK_EQ(static_cast<int>(size), static_cast<int>(data->n_blocks()));
+ const uint32_t* counts = data->counts();
+ for (size_t i = 0; i < size; ++i) {
+ CHECK_EQ(static_cast<int>(expected[i]), static_cast<int>(counts[i]));
+ }
+ }
+};
+
+
+TEST(ProfileDiamond) {
+ BasicBlockProfilerTest m;
+
+ MLabel blocka, blockb, end;
+ m.Branch(m.Parameter(0), &blocka, &blockb);
+ m.Bind(&blocka);
+ m.Goto(&end);
+ m.Bind(&blockb);
+ m.Goto(&end);
+ m.Bind(&end);
+ m.Return(m.Int32Constant(0));
+
+ m.GenerateCode();
+ {
+ uint32_t expected[] = {0, 0, 0, 0};
+ m.Expect(arraysize(expected), expected);
+ }
+
+ m.Call(0);
+ {
+ uint32_t expected[] = {1, 1, 0, 1};
+ m.Expect(arraysize(expected), expected);
+ }
+
+ m.ResetCounts();
+
+ m.Call(1);
+ {
+ uint32_t expected[] = {1, 0, 1, 1};
+ m.Expect(arraysize(expected), expected);
+ }
+
+ m.Call(0);
+ {
+ uint32_t expected[] = {2, 1, 1, 2};
+ m.Expect(arraysize(expected), expected);
+ }
+}
+
+
+TEST(ProfileLoop) {
+ BasicBlockProfilerTest m;
+
+ MLabel header, body, end;
+ Node* one = m.Int32Constant(1);
+ m.Goto(&header);
+
+ m.Bind(&header);
+ Node* count = m.Phi(kMachInt32, m.Parameter(0), one);
+ m.Branch(count, &body, &end);
+
+ m.Bind(&body);
+ count->ReplaceInput(1, m.Int32Sub(count, one));
+ m.Goto(&header);
+
+ m.Bind(&end);
+ m.Return(one);
+
+ m.GenerateCode();
+ {
+ uint32_t expected[] = {0, 0, 0, 0};
+ m.Expect(arraysize(expected), expected);
+ }
+
+ uint32_t runs[] = {0, 1, 500, 10000};
+ for (size_t i = 0; i < arraysize(runs); i++) {
+ m.ResetCounts();
+ CHECK_EQ(1, m.Call(static_cast<int>(runs[i])));
+ uint32_t expected[] = {1, runs[i] + 1, runs[i], 1};
+ m.Expect(arraysize(expected), expected);
+ }
+}
+
+#endif // V8_TURBOFAN_TARGET
diff --git a/test/cctest/compiler/test-changes-lowering.cc b/test/cctest/compiler/test-changes-lowering.cc
index 06308a0..5795754 100644
--- a/test/cctest/compiler/test-changes-lowering.cc
+++ b/test/cctest/compiler/test-changes-lowering.cc
@@ -6,10 +6,11 @@
#include "src/compiler/change-lowering.h"
#include "src/compiler/control-builders.h"
-#include "src/compiler/generic-node-inl.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-properties-inl.h"
#include "src/compiler/pipeline.h"
+#include "src/compiler/select-lowering.h"
+#include "src/compiler/simplified-lowering.h"
#include "src/compiler/typer.h"
#include "src/compiler/verifier.h"
#include "src/execution.h"
@@ -19,6 +20,7 @@
#include "src/scopes.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/codegen-tester.h"
+#include "test/cctest/compiler/function-tester.h"
#include "test/cctest/compiler/graph-builder-tester.h"
#include "test/cctest/compiler/value-helper.h"
@@ -30,13 +32,10 @@
public:
explicit ChangesLoweringTester(MachineType p0 = kMachNone)
: GraphBuilderTester<ReturnType>(p0),
- typer(this->zone()),
javascript(this->zone()),
- jsgraph(this->graph(), this->common(), &javascript, &typer,
- this->machine()),
+ jsgraph(this->graph(), this->common(), &javascript, this->machine()),
function(Handle<JSFunction>::null()) {}
- Typer typer;
JSOperatorBuilder javascript;
JSGraph jsgraph;
Handle<JSFunction> function;
@@ -45,29 +44,10 @@
template <typename T>
T* CallWithPotentialGC() {
- // TODO(titzer): we need to wrap the code in a JSFunction and call it via
- // Execution::Call() so that the GC knows about the frame, can walk it,
- // relocate the code object if necessary, etc.
- // This is pretty ugly and at the least should be moved up to helpers.
+ // TODO(titzer): we wrap the code in a JSFunction here to reuse the
+ // JSEntryStub; that could be done with a special prologue or other stub.
if (function.is_null()) {
- function =
- v8::Utils::OpenHandle(*v8::Handle<v8::Function>::Cast(CompileRun(
- "(function() { 'use strict'; return 2.7123; })")));
- CompilationInfoWithZone info(function);
- CHECK(Parser::Parse(&info));
- info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
- CHECK(Rewriter::Rewrite(&info));
- CHECK(Scope::Analyze(&info));
- CHECK_NE(NULL, info.scope());
- Handle<ScopeInfo> scope_info =
- ScopeInfo::Create(info.scope(), info.zone());
- info.shared_info()->set_scope_info(*scope_info);
- Pipeline pipeline(&info);
- Linkage linkage(&info);
- Handle<Code> code =
- pipeline.GenerateCodeForMachineGraph(&linkage, this->graph());
- CHECK(!code.is_null());
- function->ReplaceCode(*code);
+ function = FunctionTester::ForMachineGraph(this->graph());
}
Handle<Object>* args = NULL;
MaybeHandle<Object> result =
@@ -132,9 +112,9 @@
void* location) {
// We build a graph by hand here, because the raw machine assembler
// does not add the correct control and effect nodes.
- Node* load =
- this->graph()->NewNode(load_op, this->PointerConstant(location),
- this->Int32Constant(0), this->start());
+ Node* load = this->graph()->NewNode(
+ load_op, this->PointerConstant(location), this->Int32Constant(0),
+ this->start(), this->start());
Node* change = this->graph()->NewNode(op, load);
Node* ret = this->graph()->NewNode(this->common()->Return(), change,
this->start(), this->start());
@@ -146,12 +126,16 @@
void LowerChange(Node* change) {
// Run the graph reducer with changes lowering on a single node.
CompilationInfo info(this->isolate(), this->zone());
- Linkage linkage(&info);
- ChangeLowering lowering(&jsgraph, &linkage);
- GraphReducer reducer(this->graph());
- reducer.AddReducer(&lowering);
+ Linkage linkage(this->zone(), &info);
+ Typer typer(this->graph(), info.context());
+ typer.Run();
+ ChangeLowering change_lowering(&jsgraph, &linkage);
+ SelectLowering select_lowering(this->graph(), this->common());
+ GraphReducer reducer(this->graph(), this->zone());
+ reducer.AddReducer(&change_lowering);
+ reducer.AddReducer(&select_lowering);
reducer.ReduceNode(change);
- Verifier::Run(this->graph());
+ Verifier::Run(this->graph(), Verifier::UNTYPED);
}
Factory* factory() { return this->isolate()->factory(); }
diff --git a/test/cctest/compiler/test-codegen-deopt.cc b/test/cctest/compiler/test-codegen-deopt.cc
index 8217229..56afe7b 100644
--- a/test/cctest/compiler/test-codegen-deopt.cc
+++ b/test/cctest/compiler/test-codegen-deopt.cc
@@ -16,6 +16,7 @@
#include "src/compiler/register-allocator.h"
#include "src/compiler/schedule.h"
+#include "src/ast-numbering.h"
#include "src/full-codegen.h"
#include "src/parser.h"
#include "src/rewriter.h"
@@ -30,6 +31,7 @@
#if V8_TURBOFAN_TARGET
typedef RawMachineAssembler::Label MLabel;
+typedef v8::internal::compiler::InstructionSequence TestInstrSeq;
static Handle<JSFunction> NewFunction(const char* source) {
return v8::Utils::OpenHandle(
@@ -46,8 +48,7 @@
bailout_id(-1) {
CHECK(Parser::Parse(&info));
info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
- CHECK(Rewriter::Rewrite(&info));
- CHECK(Scope::Analyze(&info));
+ CHECK(Compiler::Analyze(&info));
CHECK(Compiler::EnsureDeoptimizationSupport(&info));
DCHECK(info.shared_info()->has_deoptimization_support());
@@ -55,38 +56,14 @@
graph = new (scope_->main_zone()) Graph(scope_->main_zone());
}
- virtual ~DeoptCodegenTester() { delete code; }
+ virtual ~DeoptCodegenTester() {}
void GenerateCodeFromSchedule(Schedule* schedule) {
OFStream os(stdout);
if (FLAG_trace_turbo) {
os << *schedule;
}
-
- // Initialize the codegen and generate code.
- Linkage* linkage = new (scope_->main_zone()) Linkage(&info);
- code = new v8::internal::compiler::InstructionSequence(linkage, graph,
- schedule);
- SourcePositionTable source_positions(graph);
- InstructionSelector selector(code, &source_positions);
- selector.SelectInstructions();
-
- if (FLAG_trace_turbo) {
- os << "----- Instruction sequence before register allocation -----\n"
- << *code;
- }
-
- RegisterAllocator allocator(code);
- CHECK(allocator.Allocate());
-
- if (FLAG_trace_turbo) {
- os << "----- Instruction sequence after register allocation -----\n"
- << *code;
- }
-
- compiler::CodeGenerator generator(code);
- result_code = generator.GenerateCode();
-
+ result_code = Pipeline::GenerateCodeForTesting(&info, graph, schedule);
#ifdef OBJECT_PRINT
if (FLAG_print_opt_code || FLAG_trace_turbo) {
result_code->Print();
@@ -101,7 +78,7 @@
CompilationInfo info;
BailoutId bailout_id;
Handle<Code> result_code;
- v8::internal::compiler::InstructionSequence* code;
+ TestInstrSeq* code;
Graph* graph;
};
@@ -129,13 +106,13 @@
Handle<JSFunction> deopt_function =
NewFunction("function deopt() { %DeoptimizeFunction(foo); }; deopt");
- Unique<Object> deopt_fun_constant =
- Unique<Object>::CreateUninitialized(deopt_function);
+ Unique<JSFunction> deopt_fun_constant =
+ Unique<JSFunction>::CreateUninitialized(deopt_function);
Node* deopt_fun_node = m.NewNode(common.HeapConstant(deopt_fun_constant));
Handle<Context> caller_context(function->context(), CcTest::i_isolate());
- Unique<Object> caller_context_constant =
- Unique<Object>::CreateUninitialized(caller_context);
+ Unique<Context> caller_context_constant =
+ Unique<Context>::CreateUninitialized(caller_context);
Node* caller_context_node =
m.NewNode(common.HeapConstant(caller_context_constant));
@@ -145,12 +122,13 @@
Node* stack = m.NewNode(common.StateValues(0));
Node* state_node = m.NewNode(
- common.FrameState(JS_FRAME, bailout_id, kIgnoreOutput), parameters,
- locals, stack, caller_context_node, m.UndefinedConstant());
+ common.FrameState(JS_FRAME, bailout_id,
+ OutputFrameStateCombine::Ignore()),
+ parameters, locals, stack, caller_context_node, m.UndefinedConstant());
Handle<Context> context(deopt_function->context(), CcTest::i_isolate());
- Unique<Object> context_constant =
- Unique<Object>::CreateUninitialized(context);
+ Unique<Context> context_constant =
+ Unique<Context>::CreateUninitialized(context);
Node* context_node = m.NewNode(common.HeapConstant(context_constant));
m.CallJS0(deopt_fun_node, m.UndefinedConstant(), context_node, state_node);
@@ -244,13 +222,13 @@
CSignature1<Object*, Object*> sig;
RawMachineAssembler m(graph, &sig);
- Unique<Object> this_fun_constant =
- Unique<Object>::CreateUninitialized(function);
+ Unique<HeapObject> this_fun_constant =
+ Unique<HeapObject>::CreateUninitialized(function);
Node* this_fun_node = m.NewNode(common.HeapConstant(this_fun_constant));
Handle<Context> context(function->context(), CcTest::i_isolate());
- Unique<Object> context_constant =
- Unique<Object>::CreateUninitialized(context);
+ Unique<HeapObject> context_constant =
+ Unique<HeapObject>::CreateUninitialized(context);
Node* context_node = m.NewNode(common.HeapConstant(context_constant));
bailout_id = GetCallBailoutId();
@@ -259,8 +237,9 @@
Node* stack = m.NewNode(common.StateValues(0));
Node* state_node = m.NewNode(
- common.FrameState(JS_FRAME, bailout_id, kIgnoreOutput), parameters,
- locals, stack, context_node, m.UndefinedConstant());
+ common.FrameState(JS_FRAME, bailout_id,
+ OutputFrameStateCombine::Ignore()),
+ parameters, locals, stack, context_node, m.UndefinedConstant());
m.CallRuntime1(Runtime::kDeoptimizeFunction, this_fun_node, context_node,
state_node);
diff --git a/test/cctest/compiler/test-control-reducer.cc b/test/cctest/compiler/test-control-reducer.cc
new file mode 100644
index 0000000..03aa50b
--- /dev/null
+++ b/test/cctest/compiler/test-control-reducer.cc
@@ -0,0 +1,1678 @@
+// 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/base/bits.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"
+#include "src/compiler/node-properties-inl.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+static const size_t kNumLeafs = 4;
+
+// TODO(titzer): convert this whole file into unit tests.
+
+static int CheckInputs(Node* node, Node* i0 = NULL, Node* i1 = NULL,
+ Node* i2 = NULL) {
+ int 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));
+ return count;
+}
+
+
+static int CheckMerge(Node* node, Node* i0 = NULL, Node* i1 = NULL,
+ Node* i2 = NULL) {
+ CHECK_EQ(IrOpcode::kMerge, node->opcode());
+ int count = CheckInputs(node, i0, i1, i2);
+ CHECK_EQ(count, node->op()->ControlInputCount());
+ return count;
+}
+
+
+static int CheckLoop(Node* node, Node* i0 = NULL, Node* i1 = NULL,
+ Node* i2 = NULL) {
+ CHECK_EQ(IrOpcode::kLoop, node->opcode());
+ int count = CheckInputs(node, i0, i1, i2);
+ CHECK_EQ(count, node->op()->ControlInputCount());
+ return count;
+}
+
+
+bool IsUsedBy(Node* a, Node* b) {
+ for (UseIter i = a->uses().begin(); i != a->uses().end(); ++i) {
+ if (b == *i) return true;
+ }
+ return false;
+}
+
+
+// A helper for all tests dealing with ControlTester.
+class ControlReducerTester : HandleAndZoneScope {
+ public:
+ ControlReducerTester()
+ : isolate(main_isolate()),
+ common(main_zone()),
+ graph(main_zone()),
+ jsgraph(&graph, &common, NULL, NULL),
+ start(graph.NewNode(common.Start(1))),
+ end(graph.NewNode(common.End(), start)),
+ p0(graph.NewNode(common.Parameter(0), start)),
+ zero(jsgraph.Int32Constant(0)),
+ one(jsgraph.OneConstant()),
+ half(jsgraph.Constant(0.5)),
+ self(graph.NewNode(common.Int32Constant(0xaabbccdd))),
+ dead(graph.NewNode(common.Dead())) {
+ graph.SetEnd(end);
+ graph.SetStart(start);
+ leaf[0] = zero;
+ leaf[1] = one;
+ leaf[2] = half;
+ leaf[3] = p0;
+ }
+
+ Isolate* isolate;
+ CommonOperatorBuilder common;
+ Graph graph;
+ JSGraph jsgraph;
+ Node* start;
+ Node* end;
+ Node* p0;
+ Node* zero;
+ Node* one;
+ Node* half;
+ Node* self;
+ Node* dead;
+ Node* leaf[kNumLeafs];
+
+ Node* Phi(Node* a) {
+ return SetSelfReferences(graph.NewNode(op(1, false), a, start));
+ }
+
+ Node* Phi(Node* a, Node* b) {
+ return SetSelfReferences(graph.NewNode(op(2, false), a, b, start));
+ }
+
+ Node* Phi(Node* a, Node* b, Node* c) {
+ return SetSelfReferences(graph.NewNode(op(3, false), a, b, c, start));
+ }
+
+ Node* Phi(Node* a, Node* b, Node* c, Node* d) {
+ return SetSelfReferences(graph.NewNode(op(4, false), a, b, c, d, start));
+ }
+
+ Node* EffectPhi(Node* a) {
+ return SetSelfReferences(graph.NewNode(op(1, true), a, start));
+ }
+
+ Node* EffectPhi(Node* a, Node* b) {
+ return SetSelfReferences(graph.NewNode(op(2, true), a, b, start));
+ }
+
+ Node* EffectPhi(Node* a, Node* b, Node* c) {
+ return SetSelfReferences(graph.NewNode(op(3, true), a, b, c, start));
+ }
+
+ Node* EffectPhi(Node* a, Node* b, Node* c, Node* d) {
+ return SetSelfReferences(graph.NewNode(op(4, true), a, b, c, d, start));
+ }
+
+ Node* SetSelfReferences(Node* node) {
+ for (Edge edge : node->input_edges()) {
+ if (edge.to() == self) node->ReplaceInput(edge.index(), node);
+ }
+ return node;
+ }
+
+ const Operator* op(int count, bool effect) {
+ return effect ? common.EffectPhi(count) : common.Phi(kMachAnyTagged, count);
+ }
+
+ void Trim() { ControlReducer::TrimGraph(main_zone(), &jsgraph); }
+
+ void ReduceGraph() {
+ ControlReducer::ReduceGraph(main_zone(), &jsgraph, &common);
+ }
+
+ // Checks one-step reduction of a phi.
+ void ReducePhi(Node* expect, Node* phi) {
+ Node* result = ControlReducer::ReducePhiForTesting(&jsgraph, &common, phi);
+ CHECK_EQ(expect, result);
+ ReducePhiIterative(expect, phi); // iterative should give the same result.
+ }
+
+ void ReducePhiIterative(Node* expect, Node* phi) {
+ p0->ReplaceInput(0, start); // hack: parameters may be trimmed.
+ Node* ret = graph.NewNode(common.Return(), phi, start, start);
+ Node* end = graph.NewNode(common.End(), ret);
+ graph.SetEnd(end);
+ ControlReducer::ReduceGraph(main_zone(), &jsgraph, &common);
+ CheckInputs(end, ret);
+ CheckInputs(ret, expect, start, start);
+ }
+
+ void ReduceMerge(Node* expect, Node* merge) {
+ Node* result =
+ ControlReducer::ReduceMergeForTesting(&jsgraph, &common, merge);
+ CHECK_EQ(expect, result);
+ }
+
+ void ReduceMergeIterative(Node* expect, Node* merge) {
+ p0->ReplaceInput(0, start); // hack: parameters may be trimmed.
+ Node* end = graph.NewNode(common.End(), merge);
+ graph.SetEnd(end);
+ ReduceGraph();
+ CheckInputs(end, expect);
+ }
+
+ void ReduceBranch(Node* expect, Node* branch) {
+ Node* result =
+ ControlReducer::ReduceBranchForTesting(&jsgraph, &common, branch);
+ CHECK_EQ(expect, result);
+ }
+
+ Node* Return(Node* val, Node* effect, Node* control) {
+ Node* ret = graph.NewNode(common.Return(), val, effect, control);
+ end->ReplaceInput(0, ret);
+ return ret;
+ }
+};
+
+
+TEST(Trim1_live) {
+ ControlReducerTester T;
+ CHECK(IsUsedBy(T.start, T.p0));
+ T.graph.SetEnd(T.p0);
+ T.Trim();
+ CHECK(IsUsedBy(T.start, T.p0));
+ CheckInputs(T.p0, T.start);
+}
+
+
+TEST(Trim1_dead) {
+ ControlReducerTester 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) {
+ ControlReducerTester 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));
+ CheckInputs(phi, T.one, T.half, T.start);
+}
+
+
+TEST(Trim2_dead) {
+ ControlReducerTester 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) {
+ ControlReducerTester 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) {
+ ControlReducerTester 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) {
+ ControlReducerTester 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));
+ CheckInputs(loop, T.start, loop);
+ CheckInputs(end, loop);
+}
+
+
+TEST(Trim_cycle2) {
+ ControlReducerTester 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));
+ CheckInputs(loop, T.start, loop);
+ CheckInputs(end, loop);
+
+ // 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(ControlReducerTester* 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) {
+ ControlReducerTester 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]);
+ }
+}
+
+
+TEST(CReducePhi1) {
+ ControlReducerTester R;
+
+ R.ReducePhi(R.leaf[0], R.Phi(R.leaf[0]));
+ R.ReducePhi(R.leaf[1], R.Phi(R.leaf[1]));
+ R.ReducePhi(R.leaf[2], R.Phi(R.leaf[2]));
+ R.ReducePhi(R.leaf[3], R.Phi(R.leaf[3]));
+}
+
+
+TEST(CReducePhi1_dead) {
+ ControlReducerTester R;
+
+ R.ReducePhi(R.leaf[0], R.Phi(R.leaf[0], R.dead));
+ R.ReducePhi(R.leaf[1], R.Phi(R.leaf[1], R.dead));
+ R.ReducePhi(R.leaf[2], R.Phi(R.leaf[2], R.dead));
+ R.ReducePhi(R.leaf[3], R.Phi(R.leaf[3], R.dead));
+
+ R.ReducePhi(R.leaf[0], R.Phi(R.dead, R.leaf[0]));
+ R.ReducePhi(R.leaf[1], R.Phi(R.dead, R.leaf[1]));
+ R.ReducePhi(R.leaf[2], R.Phi(R.dead, R.leaf[2]));
+ R.ReducePhi(R.leaf[3], R.Phi(R.dead, R.leaf[3]));
+}
+
+
+TEST(CReducePhi1_dead2) {
+ ControlReducerTester R;
+
+ R.ReducePhi(R.leaf[0], R.Phi(R.leaf[0], R.dead, R.dead));
+ R.ReducePhi(R.leaf[0], R.Phi(R.dead, R.leaf[0], R.dead));
+ R.ReducePhi(R.leaf[0], R.Phi(R.dead, R.dead, R.leaf[0]));
+}
+
+
+TEST(CReducePhi2a) {
+ ControlReducerTester R;
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(a, a));
+ }
+}
+
+
+TEST(CReducePhi2b) {
+ ControlReducerTester R;
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(R.self, a));
+ R.ReducePhi(a, R.Phi(a, R.self));
+ }
+}
+
+
+TEST(CReducePhi2c) {
+ ControlReducerTester R;
+
+ for (size_t i = 1; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i], *b = R.leaf[0];
+ Node* phi1 = R.Phi(b, a);
+ R.ReducePhi(phi1, phi1);
+
+ Node* phi2 = R.Phi(a, b);
+ R.ReducePhi(phi2, phi2);
+ }
+}
+
+
+TEST(CReducePhi2_dead) {
+ ControlReducerTester R;
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(a, a, R.dead));
+ R.ReducePhi(a, R.Phi(a, R.dead, a));
+ R.ReducePhi(a, R.Phi(R.dead, a, a));
+ }
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(R.self, a));
+ R.ReducePhi(a, R.Phi(a, R.self));
+ R.ReducePhi(a, R.Phi(R.self, a, R.dead));
+ R.ReducePhi(a, R.Phi(a, R.self, R.dead));
+ }
+
+ for (size_t i = 1; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i], *b = R.leaf[0];
+ Node* phi1 = R.Phi(b, a, R.dead);
+ R.ReducePhi(phi1, phi1);
+
+ Node* phi2 = R.Phi(a, b, R.dead);
+ R.ReducePhi(phi2, phi2);
+ }
+}
+
+
+TEST(CReducePhi3) {
+ ControlReducerTester R;
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(a, a, a));
+ }
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(R.self, a, a));
+ R.ReducePhi(a, R.Phi(a, R.self, a));
+ R.ReducePhi(a, R.Phi(a, a, R.self));
+ }
+
+ for (size_t i = 1; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i], *b = R.leaf[0];
+ Node* phi1 = R.Phi(b, a, a);
+ R.ReducePhi(phi1, phi1);
+
+ Node* phi2 = R.Phi(a, b, a);
+ R.ReducePhi(phi2, phi2);
+
+ Node* phi3 = R.Phi(a, a, b);
+ R.ReducePhi(phi3, phi3);
+ }
+}
+
+
+TEST(CReducePhi4) {
+ ControlReducerTester R;
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(a, a, a, a));
+ }
+
+ for (size_t i = 0; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i];
+ R.ReducePhi(a, R.Phi(R.self, a, a, a));
+ R.ReducePhi(a, R.Phi(a, R.self, a, a));
+ R.ReducePhi(a, R.Phi(a, a, R.self, a));
+ R.ReducePhi(a, R.Phi(a, a, a, R.self));
+
+ R.ReducePhi(a, R.Phi(R.self, R.self, a, a));
+ R.ReducePhi(a, R.Phi(a, R.self, R.self, a));
+ R.ReducePhi(a, R.Phi(a, a, R.self, R.self));
+ R.ReducePhi(a, R.Phi(R.self, a, a, R.self));
+ }
+
+ for (size_t i = 1; i < kNumLeafs; i++) {
+ Node* a = R.leaf[i], *b = R.leaf[0];
+ Node* phi1 = R.Phi(b, a, a, a);
+ R.ReducePhi(phi1, phi1);
+
+ Node* phi2 = R.Phi(a, b, a, a);
+ R.ReducePhi(phi2, phi2);
+
+ Node* phi3 = R.Phi(a, a, b, a);
+ R.ReducePhi(phi3, phi3);
+
+ Node* phi4 = R.Phi(a, a, a, b);
+ R.ReducePhi(phi4, phi4);
+ }
+}
+
+
+TEST(CReducePhi_iterative1) {
+ ControlReducerTester R;
+
+ R.ReducePhiIterative(R.leaf[0], R.Phi(R.leaf[0], R.Phi(R.leaf[0])));
+ R.ReducePhiIterative(R.leaf[0], R.Phi(R.Phi(R.leaf[0]), R.leaf[0]));
+}
+
+
+TEST(CReducePhi_iterative2) {
+ ControlReducerTester R;
+
+ R.ReducePhiIterative(R.leaf[0], R.Phi(R.Phi(R.leaf[0]), R.Phi(R.leaf[0])));
+}
+
+
+TEST(CReducePhi_iterative3) {
+ ControlReducerTester R;
+
+ R.ReducePhiIterative(R.leaf[0],
+ R.Phi(R.leaf[0], R.Phi(R.leaf[0], R.leaf[0])));
+ R.ReducePhiIterative(R.leaf[0],
+ R.Phi(R.Phi(R.leaf[0], R.leaf[0]), R.leaf[0]));
+}
+
+
+TEST(CReducePhi_iterative4) {
+ ControlReducerTester R;
+
+ R.ReducePhiIterative(R.leaf[0], R.Phi(R.Phi(R.leaf[0], R.leaf[0]),
+ R.Phi(R.leaf[0], R.leaf[0])));
+
+ Node* p1 = R.Phi(R.leaf[0], R.leaf[0]);
+ R.ReducePhiIterative(R.leaf[0], R.Phi(p1, p1));
+
+ Node* p2 = R.Phi(R.leaf[0], R.leaf[0], R.leaf[0]);
+ R.ReducePhiIterative(R.leaf[0], R.Phi(p2, p2, p2));
+
+ Node* p3 = R.Phi(R.leaf[0], R.leaf[0], R.leaf[0]);
+ R.ReducePhiIterative(R.leaf[0], R.Phi(p3, p3, R.leaf[0]));
+}
+
+
+TEST(CReducePhi_iterative_self1) {
+ ControlReducerTester R;
+
+ R.ReducePhiIterative(R.leaf[0], R.Phi(R.leaf[0], R.Phi(R.leaf[0], R.self)));
+ R.ReducePhiIterative(R.leaf[0], R.Phi(R.Phi(R.leaf[0], R.self), R.leaf[0]));
+}
+
+
+TEST(CReducePhi_iterative_self2) {
+ ControlReducerTester R;
+
+ R.ReducePhiIterative(
+ R.leaf[0], R.Phi(R.Phi(R.leaf[0], R.self), R.Phi(R.leaf[0], R.self)));
+ R.ReducePhiIterative(
+ R.leaf[0], R.Phi(R.Phi(R.self, R.leaf[0]), R.Phi(R.self, R.leaf[0])));
+
+ Node* p1 = R.Phi(R.leaf[0], R.self);
+ R.ReducePhiIterative(R.leaf[0], R.Phi(p1, p1));
+
+ Node* p2 = R.Phi(R.self, R.leaf[0]);
+ R.ReducePhiIterative(R.leaf[0], R.Phi(p2, p2));
+}
+
+
+TEST(EReducePhi1) {
+ ControlReducerTester R;
+
+ R.ReducePhi(R.leaf[0], R.EffectPhi(R.leaf[0]));
+ R.ReducePhi(R.leaf[1], R.EffectPhi(R.leaf[1]));
+ R.ReducePhi(R.leaf[2], R.EffectPhi(R.leaf[2]));
+ R.ReducePhi(R.leaf[3], R.EffectPhi(R.leaf[3]));
+}
+
+
+TEST(EReducePhi1_dead) {
+ ControlReducerTester R;
+
+ R.ReducePhi(R.leaf[0], R.EffectPhi(R.leaf[0], R.dead));
+ R.ReducePhi(R.leaf[1], R.EffectPhi(R.leaf[1], R.dead));
+ R.ReducePhi(R.leaf[2], R.EffectPhi(R.leaf[2], R.dead));
+ R.ReducePhi(R.leaf[3], R.EffectPhi(R.leaf[3], R.dead));
+
+ R.ReducePhi(R.leaf[0], R.EffectPhi(R.dead, R.leaf[0]));
+ R.ReducePhi(R.leaf[1], R.EffectPhi(R.dead, R.leaf[1]));
+ R.ReducePhi(R.leaf[2], R.EffectPhi(R.dead, R.leaf[2]));
+ R.ReducePhi(R.leaf[3], R.EffectPhi(R.dead, R.leaf[3]));
+}
+
+
+TEST(EReducePhi1_dead2) {
+ ControlReducerTester R;
+
+ R.ReducePhi(R.leaf[0], R.EffectPhi(R.leaf[0], R.dead, R.dead));
+ R.ReducePhi(R.leaf[0], R.EffectPhi(R.dead, R.leaf[0], R.dead));
+ R.ReducePhi(R.leaf[0], R.EffectPhi(R.dead, R.dead, R.leaf[0]));
+}
+
+
+TEST(CMergeReduce_simple1) {
+ ControlReducerTester R;
+
+ Node* merge = R.graph.NewNode(R.common.Merge(1), R.start);
+ R.ReduceMerge(R.start, merge);
+}
+
+
+TEST(CMergeReduce_simple2) {
+ ControlReducerTester R;
+
+ Node* merge1 = R.graph.NewNode(R.common.Merge(1), R.start);
+ Node* merge2 = R.graph.NewNode(R.common.Merge(1), merge1);
+ R.ReduceMerge(merge1, merge2);
+ R.ReduceMergeIterative(R.start, merge2);
+}
+
+
+TEST(CMergeReduce_none1) {
+ ControlReducerTester R;
+
+ Node* merge = R.graph.NewNode(R.common.Merge(2), R.start, R.start);
+ R.ReduceMerge(merge, merge);
+}
+
+
+TEST(CMergeReduce_none2) {
+ ControlReducerTester R;
+
+ Node* t = R.graph.NewNode(R.common.IfTrue(), R.start);
+ Node* f = R.graph.NewNode(R.common.IfFalse(), R.start);
+ Node* merge = R.graph.NewNode(R.common.Merge(2), t, f);
+ R.ReduceMerge(merge, merge);
+}
+
+
+TEST(CMergeReduce_self3) {
+ ControlReducerTester R;
+
+ Node* merge =
+ R.SetSelfReferences(R.graph.NewNode(R.common.Merge(2), R.start, R.self));
+ R.ReduceMerge(merge, merge);
+}
+
+
+TEST(CMergeReduce_dead1) {
+ ControlReducerTester R;
+
+ Node* merge = R.graph.NewNode(R.common.Merge(2), R.start, R.dead);
+ R.ReduceMerge(R.start, merge);
+}
+
+
+TEST(CMergeReduce_dead2) {
+ ControlReducerTester R;
+
+ Node* merge1 = R.graph.NewNode(R.common.Merge(1), R.start);
+ Node* merge2 = R.graph.NewNode(R.common.Merge(2), merge1, R.dead);
+ R.ReduceMerge(merge1, merge2);
+ R.ReduceMergeIterative(R.start, merge2);
+}
+
+
+TEST(CMergeReduce_dead_rm1a) {
+ ControlReducerTester R;
+
+ for (int i = 0; i < 3; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(3), R.start, R.start, R.start);
+ merge->ReplaceInput(i, R.dead);
+ R.ReduceMerge(merge, merge);
+ CheckMerge(merge, R.start, R.start);
+ }
+}
+
+
+TEST(CMergeReduce_dead_rm1b) {
+ ControlReducerTester R;
+
+ Node* t = R.graph.NewNode(R.common.IfTrue(), R.start);
+ Node* f = R.graph.NewNode(R.common.IfFalse(), R.start);
+ for (int i = 0; i < 2; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(3), R.dead, R.dead, R.dead);
+ for (int j = i + 1; j < 3; j++) {
+ merge->ReplaceInput(i, t);
+ merge->ReplaceInput(j, f);
+ R.ReduceMerge(merge, merge);
+ CheckMerge(merge, t, f);
+ }
+ }
+}
+
+
+TEST(CMergeReduce_dead_rm2) {
+ ControlReducerTester R;
+
+ for (int i = 0; i < 3; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(3), R.dead, R.dead, R.dead);
+ merge->ReplaceInput(i, R.start);
+ R.ReduceMerge(R.start, merge);
+ }
+}
+
+
+TEST(CLoopReduce_dead_rm1) {
+ ControlReducerTester R;
+
+ for (int i = 0; i < 3; i++) {
+ Node* loop = R.graph.NewNode(R.common.Loop(3), R.dead, R.start, R.start);
+ R.ReduceMerge(loop, loop);
+ CheckLoop(loop, R.start, R.start);
+ }
+}
+
+
+TEST(CMergeReduce_edit_phi1) {
+ ControlReducerTester R;
+
+ for (int i = 0; i < 3; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(3), R.start, R.start, R.start);
+ merge->ReplaceInput(i, R.dead);
+ Node* phi = R.graph.NewNode(R.common.Phi(kMachAnyTagged, 3), R.leaf[0],
+ R.leaf[1], R.leaf[2], merge);
+ R.ReduceMerge(merge, merge);
+ CHECK_EQ(IrOpcode::kPhi, phi->opcode());
+ CHECK_EQ(2, phi->op()->ValueInputCount());
+ CHECK_EQ(3, phi->InputCount());
+ CHECK_EQ(R.leaf[i < 1 ? 1 : 0], phi->InputAt(0));
+ CHECK_EQ(R.leaf[i < 2 ? 2 : 1], phi->InputAt(1));
+ CHECK_EQ(merge, phi->InputAt(2));
+ }
+}
+
+
+TEST(CMergeReduce_edit_effect_phi1) {
+ ControlReducerTester R;
+
+ for (int i = 0; i < 3; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(3), R.start, R.start, R.start);
+ merge->ReplaceInput(i, R.dead);
+ Node* phi = R.graph.NewNode(R.common.EffectPhi(3), R.leaf[0], R.leaf[1],
+ R.leaf[2], merge);
+ R.ReduceMerge(merge, merge);
+ CHECK_EQ(IrOpcode::kEffectPhi, phi->opcode());
+ CHECK_EQ(0, phi->op()->ValueInputCount());
+ CHECK_EQ(2, phi->op()->EffectInputCount());
+ CHECK_EQ(3, phi->InputCount());
+ CHECK_EQ(R.leaf[i < 1 ? 1 : 0], phi->InputAt(0));
+ CHECK_EQ(R.leaf[i < 2 ? 2 : 1], phi->InputAt(1));
+ CHECK_EQ(merge, phi->InputAt(2));
+ }
+}
+
+
+static const int kSelectorSize = 4;
+
+// Helper to select K of N nodes according to a mask, useful for the test below.
+struct Selector {
+ int mask;
+ int count;
+ explicit Selector(int m) {
+ mask = m;
+ count = v8::base::bits::CountPopulation32(m);
+ }
+ bool is_selected(int i) { return (mask & (1 << i)) != 0; }
+ void CheckNode(Node* node, IrOpcode::Value opcode, Node** inputs,
+ Node* control) {
+ CHECK_EQ(opcode, node->opcode());
+ CHECK_EQ(count + (control != NULL ? 1 : 0), node->InputCount());
+ int index = 0;
+ for (int i = 0; i < kSelectorSize; i++) {
+ if (mask & (1 << i)) {
+ CHECK_EQ(inputs[i], node->InputAt(index++));
+ }
+ }
+ CHECK_EQ(count, index);
+ if (control != NULL) CHECK_EQ(control, node->InputAt(index++));
+ }
+ int single_index() {
+ CHECK_EQ(1, count);
+ return WhichPowerOf2(mask);
+ }
+};
+
+
+TEST(CMergeReduce_exhaustive_4) {
+ ControlReducerTester R;
+ Node* controls[] = {
+ R.graph.NewNode(R.common.Start(1)), R.graph.NewNode(R.common.Start(2)),
+ R.graph.NewNode(R.common.Start(3)), R.graph.NewNode(R.common.Start(4))};
+ Node* values[] = {R.jsgraph.Int32Constant(11), R.jsgraph.Int32Constant(22),
+ R.jsgraph.Int32Constant(33), R.jsgraph.Int32Constant(44)};
+ Node* effects[] = {
+ R.jsgraph.Float64Constant(123.4), R.jsgraph.Float64Constant(223.4),
+ R.jsgraph.Float64Constant(323.4), R.jsgraph.Float64Constant(423.4)};
+
+ for (int mask = 0; mask < (1 << (kSelectorSize - 1)); mask++) {
+ // Reduce a single merge with a given mask.
+ Node* merge = R.graph.NewNode(R.common.Merge(4), controls[0], controls[1],
+ controls[2], controls[3]);
+ Node* phi = R.graph.NewNode(R.common.Phi(kMachAnyTagged, 4), values[0],
+ values[1], values[2], values[3], merge);
+ Node* ephi = R.graph.NewNode(R.common.EffectPhi(4), effects[0], effects[1],
+ effects[2], effects[3], merge);
+
+ Node* phi_use =
+ R.graph.NewNode(R.common.Phi(kMachAnyTagged, 1), phi, R.start);
+ Node* ephi_use = R.graph.NewNode(R.common.EffectPhi(1), ephi, R.start);
+
+ Selector selector(mask);
+
+ for (int i = 0; i < kSelectorSize; i++) { // set up dead merge inputs.
+ if (!selector.is_selected(i)) merge->ReplaceInput(i, R.dead);
+ }
+
+ Node* result =
+ ControlReducer::ReduceMergeForTesting(&R.jsgraph, &R.common, merge);
+
+ int count = selector.count;
+ if (count == 0) {
+ // result should be dead.
+ CHECK_EQ(IrOpcode::kDead, result->opcode());
+ } else if (count == 1) {
+ // merge should be replaced with one of the controls.
+ CHECK_EQ(controls[selector.single_index()], result);
+ // Phis should have been directly replaced.
+ CHECK_EQ(values[selector.single_index()], phi_use->InputAt(0));
+ CHECK_EQ(effects[selector.single_index()], ephi_use->InputAt(0));
+ } else {
+ // Otherwise, nodes should be edited in place.
+ CHECK_EQ(merge, result);
+ selector.CheckNode(merge, IrOpcode::kMerge, controls, NULL);
+ selector.CheckNode(phi, IrOpcode::kPhi, values, merge);
+ selector.CheckNode(ephi, IrOpcode::kEffectPhi, effects, merge);
+ CHECK_EQ(phi, phi_use->InputAt(0));
+ CHECK_EQ(ephi, ephi_use->InputAt(0));
+ CHECK_EQ(count, phi->op()->ValueInputCount());
+ CHECK_EQ(count + 1, phi->InputCount());
+ CHECK_EQ(count, ephi->op()->EffectInputCount());
+ CHECK_EQ(count + 1, ephi->InputCount());
+ }
+ }
+}
+
+
+TEST(CMergeReduce_edit_many_phis1) {
+ ControlReducerTester R;
+
+ const int kPhiCount = 10;
+ Node* phis[kPhiCount];
+
+ for (int i = 0; i < 3; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(3), R.start, R.start, R.start);
+ merge->ReplaceInput(i, R.dead);
+ for (int j = 0; j < kPhiCount; j++) {
+ phis[j] = R.graph.NewNode(R.common.Phi(kMachAnyTagged, 3), R.leaf[0],
+ R.leaf[1], R.leaf[2], merge);
+ }
+ R.ReduceMerge(merge, merge);
+ for (int j = 0; j < kPhiCount; j++) {
+ Node* phi = phis[j];
+ CHECK_EQ(IrOpcode::kPhi, phi->opcode());
+ CHECK_EQ(2, phi->op()->ValueInputCount());
+ CHECK_EQ(3, phi->InputCount());
+ CHECK_EQ(R.leaf[i < 1 ? 1 : 0], phi->InputAt(0));
+ CHECK_EQ(R.leaf[i < 2 ? 2 : 1], phi->InputAt(1));
+ CHECK_EQ(merge, phi->InputAt(2));
+ }
+ }
+}
+
+
+TEST(CMergeReduce_simple_chain1) {
+ ControlReducerTester R;
+ for (int i = 0; i < 5; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(1), R.start);
+ for (int j = 0; j < i; j++) {
+ merge = R.graph.NewNode(R.common.Merge(1), merge);
+ }
+ R.ReduceMergeIterative(R.start, merge);
+ }
+}
+
+
+TEST(CMergeReduce_dead_chain1) {
+ ControlReducerTester R;
+ for (int i = 0; i < 5; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(1), R.dead);
+ for (int j = 0; j < i; j++) {
+ merge = R.graph.NewNode(R.common.Merge(1), merge);
+ }
+ Node* end = R.graph.NewNode(R.common.End(), merge);
+ R.graph.SetEnd(end);
+ R.ReduceGraph();
+ CHECK(merge->IsDead());
+ CHECK_EQ(NULL, end->InputAt(0)); // end dies.
+ }
+}
+
+
+TEST(CMergeReduce_dead_chain2) {
+ ControlReducerTester R;
+ for (int i = 0; i < 5; i++) {
+ Node* merge = R.graph.NewNode(R.common.Merge(1), R.start);
+ for (int j = 0; j < i; j++) {
+ merge = R.graph.NewNode(R.common.Merge(2), merge, R.dead);
+ }
+ R.ReduceMergeIterative(R.start, merge);
+ }
+}
+
+
+struct Branch {
+ Node* branch;
+ Node* if_true;
+ Node* if_false;
+
+ Branch(ControlReducerTester& R, Node* cond, Node* control = NULL) {
+ if (control == NULL) control = R.start;
+ branch = R.graph.NewNode(R.common.Branch(), cond, control);
+ if_true = R.graph.NewNode(R.common.IfTrue(), branch);
+ if_false = R.graph.NewNode(R.common.IfFalse(), branch);
+ }
+};
+
+
+// TODO(titzer): use the diamonds from src/compiler/diamond.h here.
+struct Diamond {
+ Node* branch;
+ Node* if_true;
+ Node* if_false;
+ Node* merge;
+ Node* phi;
+
+ Diamond(ControlReducerTester& R, Node* cond) {
+ branch = R.graph.NewNode(R.common.Branch(), cond, R.start);
+ if_true = R.graph.NewNode(R.common.IfTrue(), branch);
+ if_false = R.graph.NewNode(R.common.IfFalse(), branch);
+ merge = R.graph.NewNode(R.common.Merge(2), if_true, if_false);
+ phi = NULL;
+ }
+
+ Diamond(ControlReducerTester& R, Node* cond, Node* tv, Node* fv) {
+ branch = R.graph.NewNode(R.common.Branch(), cond, R.start);
+ if_true = R.graph.NewNode(R.common.IfTrue(), branch);
+ if_false = R.graph.NewNode(R.common.IfFalse(), branch);
+ merge = R.graph.NewNode(R.common.Merge(2), if_true, if_false);
+ phi = R.graph.NewNode(R.common.Phi(kMachAnyTagged, 2), tv, fv, merge);
+ }
+
+ void chain(Diamond& that) { branch->ReplaceInput(1, that.merge); }
+
+ // Nest {this} into either the if_true or if_false branch of {that}.
+ void nest(Diamond& that, bool if_true) {
+ if (if_true) {
+ branch->ReplaceInput(1, that.if_true);
+ that.merge->ReplaceInput(0, merge);
+ } else {
+ branch->ReplaceInput(1, that.if_false);
+ that.merge->ReplaceInput(1, merge);
+ }
+ }
+};
+
+
+struct While {
+ Node* branch;
+ Node* if_true;
+ Node* exit;
+ Node* loop;
+
+ While(ControlReducerTester& R, Node* cond) {
+ loop = R.graph.NewNode(R.common.Loop(2), R.start, R.start);
+ branch = R.graph.NewNode(R.common.Branch(), cond, loop);
+ if_true = R.graph.NewNode(R.common.IfTrue(), branch);
+ exit = R.graph.NewNode(R.common.IfFalse(), branch);
+ loop->ReplaceInput(1, if_true);
+ }
+
+ void chain(Node* control) { loop->ReplaceInput(0, control); }
+};
+
+
+TEST(CBranchReduce_none1) {
+ ControlReducerTester R;
+ Diamond d(R, R.p0);
+ R.ReduceBranch(d.branch, d.branch);
+}
+
+
+TEST(CBranchReduce_none2) {
+ ControlReducerTester R;
+ Diamond d1(R, R.p0);
+ Diamond d2(R, R.p0);
+ d2.chain(d1);
+ R.ReduceBranch(d2.branch, d2.branch);
+}
+
+
+TEST(CBranchReduce_true) {
+ ControlReducerTester R;
+ Node* true_values[] = {
+ R.one, R.jsgraph.Int32Constant(2),
+ R.jsgraph.Int32Constant(0x7fffffff), R.jsgraph.Constant(1.0),
+ R.jsgraph.Constant(22.1), R.jsgraph.TrueConstant()};
+
+ for (size_t i = 0; i < arraysize(true_values); i++) {
+ Diamond d(R, true_values[i]);
+ Node* true_use = R.graph.NewNode(R.common.Merge(1), d.if_true);
+ Node* false_use = R.graph.NewNode(R.common.Merge(1), d.if_false);
+ R.ReduceBranch(R.start, d.branch);
+ CHECK_EQ(R.start, true_use->InputAt(0));
+ CHECK_EQ(IrOpcode::kDead, false_use->InputAt(0)->opcode());
+ CHECK(d.if_true->IsDead()); // replaced
+ CHECK(d.if_false->IsDead()); // replaced
+ }
+}
+
+
+TEST(CBranchReduce_false) {
+ ControlReducerTester R;
+ Node* false_values[] = {R.zero, R.jsgraph.Constant(0.0),
+ R.jsgraph.Constant(-0.0), R.jsgraph.FalseConstant()};
+
+ for (size_t i = 0; i < arraysize(false_values); i++) {
+ Diamond d(R, false_values[i]);
+ Node* true_use = R.graph.NewNode(R.common.Merge(1), d.if_true);
+ Node* false_use = R.graph.NewNode(R.common.Merge(1), d.if_false);
+ R.ReduceBranch(R.start, d.branch);
+ CHECK_EQ(R.start, false_use->InputAt(0));
+ CHECK_EQ(IrOpcode::kDead, true_use->InputAt(0)->opcode());
+ CHECK(d.if_true->IsDead()); // replaced
+ CHECK(d.if_false->IsDead()); // replaced
+ }
+}
+
+
+TEST(CDiamondReduce_true) {
+ ControlReducerTester R;
+ Diamond d1(R, R.one);
+ R.ReduceMergeIterative(R.start, d1.merge);
+}
+
+
+TEST(CDiamondReduce_false) {
+ ControlReducerTester R;
+ Diamond d2(R, R.zero);
+ R.ReduceMergeIterative(R.start, d2.merge);
+}
+
+
+TEST(CChainedDiamondsReduce_true_false) {
+ ControlReducerTester R;
+ Diamond d1(R, R.one);
+ Diamond d2(R, R.zero);
+ d2.chain(d1);
+
+ R.ReduceMergeIterative(R.start, d2.merge);
+}
+
+
+TEST(CChainedDiamondsReduce_x_false) {
+ ControlReducerTester R;
+ Diamond d1(R, R.p0);
+ Diamond d2(R, R.zero);
+ d2.chain(d1);
+
+ R.ReduceMergeIterative(d1.merge, d2.merge);
+}
+
+
+TEST(CChainedDiamondsReduce_false_x) {
+ ControlReducerTester R;
+ Diamond d1(R, R.zero);
+ Diamond d2(R, R.p0);
+ d2.chain(d1);
+
+ R.ReduceMergeIterative(d2.merge, d2.merge);
+ CheckInputs(d2.branch, R.p0, R.start);
+}
+
+
+TEST(CChainedDiamondsReduce_phi1) {
+ ControlReducerTester R;
+ Diamond d1(R, R.zero, R.one, R.zero); // foldable branch, phi.
+ Diamond d2(R, d1.phi);
+ d2.chain(d1);
+
+ R.ReduceMergeIterative(R.start, d2.merge);
+}
+
+
+TEST(CChainedDiamondsReduce_phi2) {
+ ControlReducerTester R;
+ Diamond d1(R, R.p0, R.one, R.one); // redundant phi.
+ Diamond d2(R, d1.phi);
+ d2.chain(d1);
+
+ R.ReduceMergeIterative(d1.merge, d2.merge);
+}
+
+
+TEST(CNestedDiamondsReduce_true_true_false) {
+ ControlReducerTester R;
+ Diamond d1(R, R.one);
+ Diamond d2(R, R.zero);
+ d2.nest(d1, true);
+
+ R.ReduceMergeIterative(R.start, d1.merge);
+}
+
+
+TEST(CNestedDiamondsReduce_false_true_false) {
+ ControlReducerTester R;
+ Diamond d1(R, R.one);
+ Diamond d2(R, R.zero);
+ d2.nest(d1, false);
+
+ R.ReduceMergeIterative(R.start, d1.merge);
+}
+
+
+TEST(CNestedDiamonds_xyz) {
+ ControlReducerTester R;
+
+ for (int a = 0; a < 2; a++) {
+ for (int b = 0; b < 2; b++) {
+ for (int c = 0; c < 2; c++) {
+ Diamond d1(R, R.jsgraph.Int32Constant(a));
+ Diamond d2(R, R.jsgraph.Int32Constant(b));
+ d2.nest(d1, c);
+
+ R.ReduceMergeIterative(R.start, d1.merge);
+ }
+ }
+ }
+}
+
+
+TEST(CDeadLoop1) {
+ ControlReducerTester R;
+
+ Node* loop = R.graph.NewNode(R.common.Loop(1), R.start);
+ Branch b(R, R.p0, loop);
+ loop->ReplaceInput(0, b.if_true); // loop is not connected to start.
+ Node* merge = R.graph.NewNode(R.common.Merge(2), R.start, b.if_false);
+ R.ReduceMergeIterative(R.start, merge);
+ CHECK(b.if_true->IsDead());
+ CHECK(b.if_false->IsDead());
+}
+
+
+TEST(CDeadLoop2) {
+ ControlReducerTester R;
+
+ While w(R, R.p0);
+ Diamond d(R, R.zero);
+ // if (0) { while (p0) ; } else { }
+ w.branch->ReplaceInput(1, d.if_true);
+ d.merge->ReplaceInput(0, w.exit);
+
+ R.ReduceMergeIterative(R.start, d.merge);
+ CHECK(d.if_true->IsDead());
+ CHECK(d.if_false->IsDead());
+}
+
+
+TEST(CNonTermLoop1) {
+ ControlReducerTester R;
+ Node* loop =
+ R.SetSelfReferences(R.graph.NewNode(R.common.Loop(2), R.start, R.self));
+ R.ReduceGraph();
+ Node* end = R.graph.end();
+ CheckLoop(loop, R.start, loop);
+ Node* merge = end->InputAt(0);
+ CheckMerge(merge, R.start, loop);
+}
+
+
+TEST(CNonTermLoop2) {
+ ControlReducerTester R;
+ Diamond d(R, R.p0);
+ Node* loop = R.SetSelfReferences(
+ R.graph.NewNode(R.common.Loop(2), d.if_false, R.self));
+ d.merge->ReplaceInput(1, R.dead);
+ Node* end = R.graph.end();
+ end->ReplaceInput(0, d.merge);
+ R.ReduceGraph();
+ CHECK_EQ(end, R.graph.end());
+ CheckLoop(loop, d.if_false, loop);
+ Node* merge = end->InputAt(0);
+ CheckMerge(merge, d.if_true, loop);
+}
+
+
+TEST(NonTermLoop3) {
+ ControlReducerTester R;
+ Node* loop = R.graph.NewNode(R.common.Loop(2), R.start, R.start);
+ Branch b(R, R.one, loop);
+ loop->ReplaceInput(1, b.if_true);
+ Node* end = R.graph.end();
+ end->ReplaceInput(0, b.if_false);
+
+ R.ReduceGraph();
+
+ CHECK_EQ(end, R.graph.end());
+ CheckInputs(end, loop);
+ CheckInputs(loop, R.start, loop);
+}
+
+
+TEST(CNonTermLoop_terminate1) {
+ ControlReducerTester R;
+ Node* loop = R.graph.NewNode(R.common.Loop(2), R.start, R.start);
+ Node* effect = R.SetSelfReferences(
+ R.graph.NewNode(R.common.EffectPhi(2), R.start, R.self, loop));
+ Branch b(R, R.one, loop);
+ loop->ReplaceInput(1, b.if_true);
+ Node* end = R.graph.end();
+ end->ReplaceInput(0, b.if_false);
+
+ R.ReduceGraph();
+
+ CHECK_EQ(end, R.graph.end());
+ CheckLoop(loop, R.start, loop);
+ Node* terminate = end->InputAt(0);
+ CHECK_EQ(IrOpcode::kTerminate, terminate->opcode());
+ CHECK_EQ(2, terminate->InputCount());
+ CHECK_EQ(1, terminate->op()->EffectInputCount());
+ CHECK_EQ(1, terminate->op()->ControlInputCount());
+ CheckInputs(terminate, effect, loop);
+}
+
+
+TEST(CNonTermLoop_terminate2) {
+ ControlReducerTester R;
+ Node* loop = R.graph.NewNode(R.common.Loop(2), R.start, R.start);
+ Node* effect1 = R.SetSelfReferences(
+ R.graph.NewNode(R.common.EffectPhi(2), R.start, R.self, loop));
+ Node* effect2 = R.SetSelfReferences(
+ R.graph.NewNode(R.common.EffectPhi(2), R.start, R.self, loop));
+ Branch b(R, R.one, loop);
+ loop->ReplaceInput(1, b.if_true);
+ Node* end = R.graph.end();
+ end->ReplaceInput(0, b.if_false);
+
+ R.ReduceGraph();
+
+ CheckLoop(loop, R.start, loop);
+ CHECK_EQ(end, R.graph.end());
+ Node* terminate = end->InputAt(0);
+ CHECK_EQ(IrOpcode::kTerminate, terminate->opcode());
+ CHECK_EQ(3, terminate->InputCount());
+ CHECK_EQ(2, terminate->op()->EffectInputCount());
+ CHECK_EQ(1, terminate->op()->ControlInputCount());
+ Node* e0 = terminate->InputAt(0);
+ Node* e1 = terminate->InputAt(1);
+ CHECK(e0 == effect1 || e1 == effect1);
+ CHECK(e0 == effect2 || e1 == effect2);
+ CHECK_EQ(loop, terminate->InputAt(2));
+}
+
+
+TEST(CNonTermLoop_terminate_m1) {
+ ControlReducerTester R;
+ Node* loop =
+ R.SetSelfReferences(R.graph.NewNode(R.common.Loop(2), R.start, R.self));
+ Node* effect = R.SetSelfReferences(
+ R.graph.NewNode(R.common.EffectPhi(2), R.start, R.self, loop));
+ R.ReduceGraph();
+ Node* end = R.graph.end();
+ CHECK_EQ(R.start, loop->InputAt(0));
+ CHECK_EQ(loop, loop->InputAt(1));
+ Node* merge = end->InputAt(0);
+ CHECK_EQ(IrOpcode::kMerge, merge->opcode());
+ CHECK_EQ(2, merge->InputCount());
+ CHECK_EQ(2, merge->op()->ControlInputCount());
+ CHECK_EQ(R.start, merge->InputAt(0));
+
+ Node* terminate = merge->InputAt(1);
+ CHECK_EQ(IrOpcode::kTerminate, terminate->opcode());
+ CHECK_EQ(2, terminate->InputCount());
+ CHECK_EQ(1, terminate->op()->EffectInputCount());
+ CHECK_EQ(1, terminate->op()->ControlInputCount());
+ CHECK_EQ(effect, terminate->InputAt(0));
+ CHECK_EQ(loop, terminate->InputAt(1));
+}
+
+
+TEST(CNonTermLoop_big1) {
+ ControlReducerTester R;
+ Branch b1(R, R.p0);
+ Node* rt = R.graph.NewNode(R.common.Return(), R.one, R.start, b1.if_true);
+
+ Branch b2(R, R.p0, b1.if_false);
+ Node* rf = R.graph.NewNode(R.common.Return(), R.zero, R.start, b2.if_true);
+ Node* loop = R.SetSelfReferences(
+ R.graph.NewNode(R.common.Loop(2), b2.if_false, R.self));
+ Node* merge = R.graph.NewNode(R.common.Merge(2), rt, rf);
+ R.end->ReplaceInput(0, merge);
+
+ R.ReduceGraph();
+
+ CheckInputs(R.end, merge);
+ CheckInputs(merge, rt, rf, loop);
+ CheckInputs(loop, b2.if_false, loop);
+}
+
+
+TEST(CNonTermLoop_big2) {
+ ControlReducerTester R;
+ Branch b1(R, R.p0);
+ Node* rt = R.graph.NewNode(R.common.Return(), R.one, R.start, b1.if_true);
+
+ Branch b2(R, R.zero, b1.if_false);
+ Node* rf = R.graph.NewNode(R.common.Return(), R.zero, R.start, b2.if_true);
+ Node* loop = R.SetSelfReferences(
+ R.graph.NewNode(R.common.Loop(2), b2.if_false, R.self));
+ Node* merge = R.graph.NewNode(R.common.Merge(2), rt, rf);
+ R.end->ReplaceInput(0, merge);
+
+ R.ReduceGraph();
+
+ Node* new_merge = R.end->InputAt(0); // old merge was reduced.
+ CHECK_NE(merge, new_merge);
+ CheckInputs(new_merge, rt, loop);
+ CheckInputs(loop, b1.if_false, loop);
+ CHECK(merge->IsDead());
+ CHECK(rf->IsDead());
+ CHECK(b2.if_true->IsDead());
+}
+
+
+TEST(Return1) {
+ ControlReducerTester R;
+ Node* ret = R.Return(R.one, R.start, R.start);
+ R.ReduceGraph();
+ CheckInputs(R.graph.end(), ret);
+ CheckInputs(ret, R.one, R.start, R.start);
+}
+
+
+TEST(Return2) {
+ ControlReducerTester R;
+ Diamond d(R, R.one);
+ Node* ret = R.Return(R.half, R.start, d.merge);
+ R.ReduceGraph();
+ CHECK(d.branch->IsDead());
+ CHECK(d.if_true->IsDead());
+ CHECK(d.if_false->IsDead());
+ CHECK(d.merge->IsDead());
+
+ CheckInputs(R.graph.end(), ret);
+ CheckInputs(ret, R.half, R.start, R.start);
+}
+
+
+TEST(Return_true1) {
+ ControlReducerTester R;
+ Diamond d(R, R.one, R.half, R.zero);
+ Node* ret = R.Return(d.phi, R.start, d.merge);
+ R.ReduceGraph();
+ CHECK(d.branch->IsDead());
+ CHECK(d.if_true->IsDead());
+ CHECK(d.if_false->IsDead());
+ CHECK(d.merge->IsDead());
+ CHECK(d.phi->IsDead());
+
+ CheckInputs(R.graph.end(), ret);
+ CheckInputs(ret, R.half, R.start, R.start);
+}
+
+
+TEST(Return_false1) {
+ ControlReducerTester R;
+ Diamond d(R, R.zero, R.one, R.half);
+ Node* ret = R.Return(d.phi, R.start, d.merge);
+ R.ReduceGraph();
+ CHECK(d.branch->IsDead());
+ CHECK(d.if_true->IsDead());
+ CHECK(d.if_false->IsDead());
+ CHECK(d.merge->IsDead());
+ CHECK(d.phi->IsDead());
+
+ CheckInputs(R.graph.end(), ret);
+ CheckInputs(ret, R.half, R.start, R.start);
+}
+
+
+void CheckDeadDiamond(Diamond& d) {
+ CHECK(d.branch->IsDead());
+ CHECK(d.if_true->IsDead());
+ CHECK(d.if_false->IsDead());
+ CHECK(d.merge->IsDead());
+ if (d.phi != NULL) CHECK(d.phi->IsDead());
+}
+
+
+void CheckLiveDiamond(Diamond& d, bool live_phi = true) {
+ CheckInputs(d.merge, d.if_true, d.if_false);
+ CheckInputs(d.if_true, d.branch);
+ CheckInputs(d.if_false, d.branch);
+ if (d.phi != NULL) {
+ if (live_phi) {
+ CHECK_EQ(3, d.phi->InputCount());
+ CHECK_EQ(d.merge, d.phi->InputAt(2));
+ } else {
+ CHECK(d.phi->IsDead());
+ }
+ }
+}
+
+
+TEST(Return_effect1) {
+ ControlReducerTester R;
+ Diamond d(R, R.one);
+ Node* e1 = R.jsgraph.Float64Constant(-100.1);
+ Node* e2 = R.jsgraph.Float64Constant(+100.1);
+ Node* effect = R.graph.NewNode(R.common.EffectPhi(2), e1, e2, d.merge);
+ Node* ret = R.Return(R.p0, effect, d.merge);
+ R.ReduceGraph();
+ CheckDeadDiamond(d);
+ CHECK(effect->IsDead());
+
+ CheckInputs(R.graph.end(), ret);
+ CheckInputs(ret, R.p0, e1, R.start);
+}
+
+
+TEST(Return_nested_diamonds1) {
+ ControlReducerTester R;
+ Diamond d1(R, R.p0, R.one, R.zero);
+ Diamond d2(R, R.p0);
+ Diamond d3(R, R.p0);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // nothing should happen.
+
+ CheckInputs(ret, d1.phi, R.start, d1.merge);
+ CheckInputs(d1.phi, R.one, R.zero, d1.merge);
+ CheckInputs(d1.merge, d2.merge, d3.merge);
+ CheckLiveDiamond(d2);
+ CheckLiveDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds_true1) {
+ ControlReducerTester R;
+ Diamond d1(R, R.one, R.one, R.zero);
+ Diamond d2(R, R.p0);
+ Diamond d3(R, R.p0);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // d1 gets folded true.
+
+ CheckInputs(ret, R.one, R.start, d2.merge);
+ CheckInputs(d2.branch, R.p0, R.start);
+ CheckDeadDiamond(d1);
+ CheckLiveDiamond(d2);
+ CheckDeadDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds_false1) {
+ ControlReducerTester R;
+ Diamond d1(R, R.zero, R.one, R.zero);
+ Diamond d2(R, R.p0);
+ Diamond d3(R, R.p0);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // d1 gets folded false.
+
+ CheckInputs(ret, R.zero, R.start, d3.merge);
+ CheckInputs(d3.branch, R.p0, R.start);
+ CheckDeadDiamond(d1);
+ CheckDeadDiamond(d2);
+ CheckLiveDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds_true_true1) {
+ ControlReducerTester R;
+ Diamond d1(R, R.one, R.one, R.zero);
+ Diamond d2(R, R.one);
+ Diamond d3(R, R.p0);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // d1 and d2 both get folded true.
+
+ CheckInputs(ret, R.one, R.start, R.start);
+ CheckDeadDiamond(d1);
+ CheckDeadDiamond(d2);
+ CheckDeadDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds_true_false1) {
+ ControlReducerTester R;
+ Diamond d1(R, R.one, R.one, R.zero);
+ Diamond d2(R, R.zero);
+ Diamond d3(R, R.p0);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // d1 gets folded true and d2 gets folded false.
+
+ CheckInputs(ret, R.one, R.start, R.start);
+ CheckDeadDiamond(d1);
+ CheckDeadDiamond(d2);
+ CheckDeadDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds2) {
+ ControlReducerTester R;
+ Node* x2 = R.jsgraph.Float64Constant(11.1);
+ Node* y2 = R.jsgraph.Float64Constant(22.2);
+ Node* x3 = R.jsgraph.Float64Constant(33.3);
+ Node* y3 = R.jsgraph.Float64Constant(44.4);
+
+ Diamond d2(R, R.p0, x2, y2);
+ Diamond d3(R, R.p0, x3, y3);
+ Diamond d1(R, R.p0, d2.phi, d3.phi);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // nothing should happen.
+
+ CheckInputs(ret, d1.phi, R.start, d1.merge);
+ CheckInputs(d1.phi, d2.phi, d3.phi, d1.merge);
+ CheckInputs(d1.merge, d2.merge, d3.merge);
+ CheckLiveDiamond(d2);
+ CheckLiveDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds_true2) {
+ ControlReducerTester R;
+ Node* x2 = R.jsgraph.Float64Constant(11.1);
+ Node* y2 = R.jsgraph.Float64Constant(22.2);
+ Node* x3 = R.jsgraph.Float64Constant(33.3);
+ Node* y3 = R.jsgraph.Float64Constant(44.4);
+
+ Diamond d2(R, R.p0, x2, y2);
+ Diamond d3(R, R.p0, x3, y3);
+ Diamond d1(R, R.one, d2.phi, d3.phi);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // d1 gets folded true.
+
+ CheckInputs(ret, d2.phi, R.start, d2.merge);
+ CheckInputs(d2.branch, R.p0, R.start);
+ CheckDeadDiamond(d1);
+ CheckLiveDiamond(d2);
+ CheckDeadDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds_true_true2) {
+ ControlReducerTester R;
+ Node* x2 = R.jsgraph.Float64Constant(11.1);
+ Node* y2 = R.jsgraph.Float64Constant(22.2);
+ Node* x3 = R.jsgraph.Float64Constant(33.3);
+ Node* y3 = R.jsgraph.Float64Constant(44.4);
+
+ Diamond d2(R, R.one, x2, y2);
+ Diamond d3(R, R.p0, x3, y3);
+ Diamond d1(R, R.one, d2.phi, d3.phi);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // d1 gets folded true.
+
+ CheckInputs(ret, x2, R.start, R.start);
+ CheckDeadDiamond(d1);
+ CheckDeadDiamond(d2);
+ CheckDeadDiamond(d3);
+}
+
+
+TEST(Return_nested_diamonds_true_false2) {
+ ControlReducerTester R;
+ Node* x2 = R.jsgraph.Float64Constant(11.1);
+ Node* y2 = R.jsgraph.Float64Constant(22.2);
+ Node* x3 = R.jsgraph.Float64Constant(33.3);
+ Node* y3 = R.jsgraph.Float64Constant(44.4);
+
+ Diamond d2(R, R.zero, x2, y2);
+ Diamond d3(R, R.p0, x3, y3);
+ Diamond d1(R, R.one, d2.phi, d3.phi);
+
+ d2.nest(d1, true);
+ d3.nest(d1, false);
+
+ Node* ret = R.Return(d1.phi, R.start, d1.merge);
+
+ R.ReduceGraph(); // d1 gets folded true.
+
+ CheckInputs(ret, y2, R.start, R.start);
+ CheckDeadDiamond(d1);
+ CheckDeadDiamond(d2);
+ CheckDeadDiamond(d3);
+}
diff --git a/test/cctest/compiler/test-gap-resolver.cc b/test/cctest/compiler/test-gap-resolver.cc
index 6239f2a..ea6f4ee 100644
--- a/test/cctest/compiler/test-gap-resolver.cc
+++ b/test/cctest/compiler/test-gap-resolver.cc
@@ -58,13 +58,16 @@
return Value(op->kind(), op->index());
}
- friend OStream& operator<<(OStream& os, const InterpreterState& is) {
+ friend std::ostream& operator<<(std::ostream& os,
+ const InterpreterState& is) {
for (OperandMap::const_iterator it = is.values_.begin();
it != is.values_.end(); ++it) {
if (it != is.values_.begin()) os << " ";
InstructionOperand source(it->first.first, it->first.second);
InstructionOperand destination(it->second.first, it->second.second);
- os << MoveOperands(&source, &destination);
+ MoveOperands mo(&source, &destination);
+ PrintableMoveOperands pmo = {RegisterConfiguration::ArchDefault(), &mo};
+ os << pmo;
}
return os;
}
diff --git a/test/cctest/compiler/test-graph-reducer.cc b/test/cctest/compiler/test-graph-reducer.cc
index eabfd22..70b57b9 100644
--- a/test/cctest/compiler/test-graph-reducer.cc
+++ b/test/cctest/compiler/test-graph-reducer.cc
@@ -5,7 +5,6 @@
#include "src/v8.h"
#include "graph-tester.h"
-#include "src/compiler/generic-node-inl.h"
#include "src/compiler/graph-reducer.h"
using namespace v8::internal;
@@ -21,15 +20,15 @@
const uint8_t OPCODE_C1 = 31;
const uint8_t OPCODE_C2 = 32;
-static SimpleOperator OPA0(OPCODE_A0, Operator::kNoWrite, 0, 0, "opa0");
-static SimpleOperator OPA1(OPCODE_A1, Operator::kNoWrite, 1, 0, "opa1");
-static SimpleOperator OPA2(OPCODE_A2, Operator::kNoWrite, 2, 0, "opa2");
-static SimpleOperator OPB0(OPCODE_B0, Operator::kNoWrite, 0, 0, "opa0");
-static SimpleOperator OPB1(OPCODE_B1, Operator::kNoWrite, 1, 0, "opa1");
-static SimpleOperator OPB2(OPCODE_B2, Operator::kNoWrite, 2, 0, "opa2");
-static SimpleOperator OPC0(OPCODE_C0, Operator::kNoWrite, 0, 0, "opc0");
-static SimpleOperator OPC1(OPCODE_C1, Operator::kNoWrite, 1, 0, "opc1");
-static SimpleOperator OPC2(OPCODE_C2, Operator::kNoWrite, 2, 0, "opc2");
+static Operator OPA0(OPCODE_A0, Operator::kNoWrite, "opa0", 0, 0, 0, 0, 0, 0);
+static Operator OPA1(OPCODE_A1, Operator::kNoWrite, "opa1", 1, 0, 0, 0, 0, 0);
+static Operator OPA2(OPCODE_A2, Operator::kNoWrite, "opa2", 2, 0, 0, 0, 0, 0);
+static Operator OPB0(OPCODE_B0, Operator::kNoWrite, "opa0", 0, 0, 0, 0, 0, 0);
+static Operator OPB1(OPCODE_B1, Operator::kNoWrite, "opa1", 1, 0, 0, 0, 0, 0);
+static Operator OPB2(OPCODE_B2, Operator::kNoWrite, "opa2", 2, 0, 0, 0, 0, 0);
+static Operator OPC0(OPCODE_C0, Operator::kNoWrite, "opc0", 0, 0, 0, 0, 0, 0);
+static Operator OPC1(OPCODE_C1, Operator::kNoWrite, "opc1", 1, 0, 0, 0, 0, 0);
+static Operator OPC2(OPCODE_C2, Operator::kNoWrite, "opc2", 2, 0, 0, 0, 0, 0);
// Replaces all "A" operators with "B" operators without creating new nodes.
@@ -202,7 +201,7 @@
Node* end = graph.NewNode(&OPA1, n1);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
ReducerRecorder recorder(graph.zone());
reducer.AddReducer(&recorder);
reducer.ReduceGraph();
@@ -220,7 +219,7 @@
Node* end = graph.NewNode(&OPA2, n2, n3);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
ReducerRecorder recorder(graph.zone());
reducer.AddReducer(&recorder);
reducer.ReduceGraph();
@@ -238,7 +237,7 @@
Node* end = graph.NewNode(&OPA1, n1);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
InPlaceABReducer r;
reducer.AddReducer(&r);
@@ -263,7 +262,7 @@
Node* end = graph.NewNode(&OPA2, n2, n3);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
InPlaceABReducer r;
reducer.AddReducer(&r);
@@ -293,7 +292,7 @@
Node* end = graph.NewNode(&OPA2, n2, n3);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
NewABReducer r(&graph);
reducer.AddReducer(&r);
@@ -330,7 +329,7 @@
graph.SetEnd(end);
CHECK_EQ(1, graph.NodeCount());
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
A0Wrapper r(&graph);
reducer.AddReducer(&r);
@@ -352,7 +351,7 @@
graph.SetEnd(end);
CHECK_EQ(1, graph.NodeCount());
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
B0Wrapper r(&graph);
reducer.AddReducer(&r);
@@ -379,7 +378,7 @@
Node* end = graph.NewNode(&OPA1, n1);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
A1Forwarder r;
reducer.AddReducer(&r);
@@ -403,7 +402,7 @@
Node* end = graph.NewNode(&OPA2, n2, n3);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
A1Forwarder r;
reducer.AddReducer(&r);
@@ -434,7 +433,7 @@
}
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
A1Forwarder r;
reducer.AddReducer(&r);
@@ -458,7 +457,7 @@
Node* end = graph.NewNode(&OPA2, n2, n3);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
InPlaceABReducer r;
B1Forwarder f;
reducer.AddReducer(&r);
@@ -501,7 +500,7 @@
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
reducer.AddReducer(&r);
int before = graph.NodeCount();
@@ -560,7 +559,7 @@
GenDAG(&graph, p3, p2, p1);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
AB2Sorter r1;
A1Forwarder r2;
InPlaceABReducer r3;
@@ -599,7 +598,7 @@
Node* end = graph.NewNode(&OPA1, n1);
graph.SetEnd(end);
- GraphReducer reducer(&graph);
+ GraphReducer reducer(&graph, graph.zone());
InPlaceABReducer abr;
InPlaceBCReducer bcr;
if (i == 0) {
@@ -621,41 +620,3 @@
}
}
}
-
-
-// Tests that a reducer is only applied once.
-class OneTimeReducer : public Reducer {
- public:
- OneTimeReducer(Reducer* reducer, Zone* zone)
- : reducer_(reducer),
- nodes_(NodeSet::key_compare(), NodeSet::allocator_type(zone)) {}
- virtual Reduction Reduce(Node* node) {
- CHECK_EQ(0, static_cast<int>(nodes_.count(node)));
- nodes_.insert(node);
- return reducer_->Reduce(node);
- }
- Reducer* reducer_;
- NodeSet nodes_;
-};
-
-
-TEST(OneTimeReduce1) {
- GraphTester graph;
-
- Node* n1 = graph.NewNode(&OPA0);
- Node* end = graph.NewNode(&OPA1, n1);
- graph.SetEnd(end);
-
- GraphReducer reducer(&graph);
- InPlaceABReducer r;
- OneTimeReducer once(&r, graph.zone());
- reducer.AddReducer(&once);
-
- // Tests A* => B* with in-place updates. Should only be applied once.
- int before = graph.NodeCount();
- reducer.ReduceGraph();
- CHECK_EQ(before, graph.NodeCount());
- CHECK_EQ(&OPB0, n1->op());
- CHECK_EQ(&OPB1, end->op());
- CHECK_EQ(n1, end->InputAt(0));
-}
diff --git a/test/cctest/compiler/test-graph-visualizer.cc b/test/cctest/compiler/test-graph-visualizer.cc
new file mode 100644
index 0000000..ce3e6b7
--- /dev/null
+++ b/test/cctest/compiler/test-graph-visualizer.cc
@@ -0,0 +1,90 @@
+// 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/graph.h"
+#include "src/compiler/graph-visualizer.h"
+#include "src/compiler/js-operator.h"
+#include "src/compiler/machine-operator.h"
+#include "src/compiler/node.h"
+#include "src/compiler/operator.h"
+#include "src/compiler/schedule.h"
+#include "src/compiler/scheduler.h"
+#include "src/compiler/verifier.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+TEST(NodeWithNullInputReachableFromEnd) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(0));
+ graph.SetStart(start);
+ Node* k = graph.NewNode(common.Int32Constant(0));
+ Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 1), k, start);
+ phi->ReplaceInput(0, NULL);
+ graph.SetEnd(phi);
+
+ OFStream os(stdout);
+ os << AsDOT(graph);
+ os << AsJSON(graph);
+}
+
+
+TEST(NodeWithNullControlReachableFromEnd) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(0));
+ graph.SetStart(start);
+ Node* k = graph.NewNode(common.Int32Constant(0));
+ Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 1), k, start);
+ phi->ReplaceInput(1, NULL);
+ graph.SetEnd(phi);
+
+ OFStream os(stdout);
+ os << AsDOT(graph);
+ os << AsJSON(graph);
+}
+
+
+TEST(NodeWithNullInputReachableFromStart) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(0));
+ graph.SetStart(start);
+ Node* k = graph.NewNode(common.Int32Constant(0));
+ Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 1), k, start);
+ phi->ReplaceInput(0, NULL);
+ graph.SetEnd(start);
+
+ OFStream os(stdout);
+ os << AsDOT(graph);
+ os << AsJSON(graph);
+}
+
+
+TEST(NodeWithNullControlReachableFromStart) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(0));
+ graph.SetStart(start);
+ Node* merge = graph.NewNode(common.Merge(2), start, start);
+ merge->ReplaceInput(1, NULL);
+ graph.SetEnd(merge);
+
+ OFStream os(stdout);
+ os << AsDOT(graph);
+ os << AsJSON(graph);
+}
diff --git a/test/cctest/compiler/test-instruction.cc b/test/cctest/compiler/test-instruction.cc
index a9feaac..294812f 100644
--- a/test/cctest/compiler/test-instruction.cc
+++ b/test/cctest/compiler/test-instruction.cc
@@ -31,12 +31,11 @@
graph(zone()),
schedule(zone()),
info(static_cast<HydrogenCodeStub*>(NULL), main_isolate()),
- linkage(&info),
+ linkage(zone(), &info),
common(zone()),
+ machine(zone()),
code(NULL) {}
- ~InstructionTester() { delete code; }
-
Isolate* isolate;
Graph graph;
Schedule schedule;
@@ -51,10 +50,12 @@
void allocCode() {
if (schedule.rpo_order()->size() == 0) {
// Compute the RPO order.
- Scheduler::ComputeSpecialRPO(&schedule);
+ Scheduler::ComputeSpecialRPO(main_zone(), &schedule);
DCHECK(schedule.rpo_order()->size() > 0);
}
- code = new TestInstrSeq(&linkage, &graph, &schedule);
+ InstructionBlocks* instruction_blocks =
+ TestInstrSeq::InstructionBlocksFor(main_zone(), &schedule);
+ code = new (main_zone()) TestInstrSeq(main_zone(), instruction_blocks);
}
Node* Int32Constant(int32_t val) {
@@ -81,10 +82,10 @@
return node;
}
- int NewInstr(BasicBlock* block) {
+ int NewInstr() {
InstructionCode opcode = static_cast<InstructionCode>(110);
TestInstr* instr = TestInstr::New(zone(), opcode);
- return code->AddInstruction(instr, block);
+ return code->AddInstruction(instr);
}
UnallocatedOperand* NewUnallocated(int vreg) {
@@ -93,6 +94,21 @@
unallocated->set_virtual_register(vreg);
return unallocated;
}
+
+ InstructionBlock* BlockAt(BasicBlock* block) {
+ return code->InstructionBlockAt(block->GetRpoNumber());
+ }
+ BasicBlock* GetBasicBlock(int instruction_index) {
+ const InstructionBlock* block =
+ code->GetInstructionBlock(instruction_index);
+ return schedule.rpo_order()->at(block->rpo_number().ToSize());
+ }
+ int first_instruction_index(BasicBlock* block) {
+ return BlockAt(block)->first_instruction_index();
+ }
+ int last_instruction_index(BasicBlock* block) {
+ return BlockAt(block)->last_instruction_index();
+ }
};
@@ -112,17 +128,16 @@
R.allocCode();
- CHECK_EQ(R.graph.NodeCount(), R.code->ValueCount());
-
BasicBlockVector* blocks = R.schedule.rpo_order();
- CHECK_EQ(static_cast<int>(blocks->size()), R.code->BasicBlockCount());
+ CHECK_EQ(static_cast<int>(blocks->size()), R.code->InstructionBlockCount());
int index = 0;
for (BasicBlockVectorIter i = blocks->begin(); i != blocks->end();
i++, index++) {
BasicBlock* block = *i;
- CHECK_EQ(block, R.code->BlockAt(index));
- CHECK_EQ(-1, R.code->GetLoopEnd(block));
+ CHECK_EQ(block->rpo_number(), R.BlockAt(block)->rpo_number().ToInt());
+ CHECK_EQ(block->id().ToInt(), R.BlockAt(block)->id().ToInt());
+ CHECK_EQ(NULL, block->loop_end());
}
}
@@ -141,47 +156,47 @@
R.allocCode();
- R.code->StartBlock(b0);
- int i0 = R.NewInstr(b0);
- int i1 = R.NewInstr(b0);
- R.code->EndBlock(b0);
- R.code->StartBlock(b1);
- int i2 = R.NewInstr(b1);
- int i3 = R.NewInstr(b1);
- int i4 = R.NewInstr(b1);
- int i5 = R.NewInstr(b1);
- R.code->EndBlock(b1);
- R.code->StartBlock(b2);
- int i6 = R.NewInstr(b2);
- int i7 = R.NewInstr(b2);
- int i8 = R.NewInstr(b2);
- R.code->EndBlock(b2);
- R.code->StartBlock(b3);
- R.code->EndBlock(b3);
+ R.code->StartBlock(b0->GetRpoNumber());
+ int i0 = R.NewInstr();
+ int i1 = R.NewInstr();
+ R.code->EndBlock(b0->GetRpoNumber());
+ R.code->StartBlock(b1->GetRpoNumber());
+ int i2 = R.NewInstr();
+ int i3 = R.NewInstr();
+ int i4 = R.NewInstr();
+ int i5 = R.NewInstr();
+ R.code->EndBlock(b1->GetRpoNumber());
+ R.code->StartBlock(b2->GetRpoNumber());
+ int i6 = R.NewInstr();
+ int i7 = R.NewInstr();
+ int i8 = R.NewInstr();
+ R.code->EndBlock(b2->GetRpoNumber());
+ R.code->StartBlock(b3->GetRpoNumber());
+ R.code->EndBlock(b3->GetRpoNumber());
- CHECK_EQ(b0, R.code->GetBasicBlock(i0));
- CHECK_EQ(b0, R.code->GetBasicBlock(i1));
+ CHECK_EQ(b0, R.GetBasicBlock(i0));
+ CHECK_EQ(b0, R.GetBasicBlock(i1));
- CHECK_EQ(b1, R.code->GetBasicBlock(i2));
- CHECK_EQ(b1, R.code->GetBasicBlock(i3));
- CHECK_EQ(b1, R.code->GetBasicBlock(i4));
- CHECK_EQ(b1, R.code->GetBasicBlock(i5));
+ CHECK_EQ(b1, R.GetBasicBlock(i2));
+ CHECK_EQ(b1, R.GetBasicBlock(i3));
+ CHECK_EQ(b1, R.GetBasicBlock(i4));
+ CHECK_EQ(b1, R.GetBasicBlock(i5));
- CHECK_EQ(b2, R.code->GetBasicBlock(i6));
- CHECK_EQ(b2, R.code->GetBasicBlock(i7));
- CHECK_EQ(b2, R.code->GetBasicBlock(i8));
+ CHECK_EQ(b2, R.GetBasicBlock(i6));
+ CHECK_EQ(b2, R.GetBasicBlock(i7));
+ CHECK_EQ(b2, R.GetBasicBlock(i8));
- CHECK_EQ(b0, R.code->GetBasicBlock(b0->first_instruction_index()));
- CHECK_EQ(b0, R.code->GetBasicBlock(b0->last_instruction_index()));
+ CHECK_EQ(b0, R.GetBasicBlock(R.first_instruction_index(b0)));
+ CHECK_EQ(b0, R.GetBasicBlock(R.last_instruction_index(b0)));
- CHECK_EQ(b1, R.code->GetBasicBlock(b1->first_instruction_index()));
- CHECK_EQ(b1, R.code->GetBasicBlock(b1->last_instruction_index()));
+ CHECK_EQ(b1, R.GetBasicBlock(R.first_instruction_index(b1)));
+ CHECK_EQ(b1, R.GetBasicBlock(R.last_instruction_index(b1)));
- CHECK_EQ(b2, R.code->GetBasicBlock(b2->first_instruction_index()));
- CHECK_EQ(b2, R.code->GetBasicBlock(b2->last_instruction_index()));
+ CHECK_EQ(b2, R.GetBasicBlock(R.first_instruction_index(b2)));
+ CHECK_EQ(b2, R.GetBasicBlock(R.last_instruction_index(b2)));
- CHECK_EQ(b3, R.code->GetBasicBlock(b3->first_instruction_index()));
- CHECK_EQ(b3, R.code->GetBasicBlock(b3->last_instruction_index()));
+ CHECK_EQ(b3, R.GetBasicBlock(R.first_instruction_index(b3)));
+ CHECK_EQ(b3, R.GetBasicBlock(R.last_instruction_index(b3)));
}
@@ -194,10 +209,10 @@
R.allocCode();
TestInstr* i0 = TestInstr::New(R.zone(), 100);
TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
- R.code->StartBlock(b0);
- R.code->AddInstruction(i0, b0);
- R.code->AddInstruction(g, b0);
- R.code->EndBlock(b0);
+ R.code->StartBlock(b0->GetRpoNumber());
+ R.code->AddInstruction(i0);
+ R.code->AddInstruction(g);
+ R.code->EndBlock(b0->GetRpoNumber());
CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
@@ -221,17 +236,17 @@
R.allocCode();
TestInstr* i0 = TestInstr::New(R.zone(), 100);
TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
- R.code->StartBlock(b0);
- R.code->AddInstruction(i0, b0);
- R.code->AddInstruction(g, b0);
- R.code->EndBlock(b0);
+ R.code->StartBlock(b0->GetRpoNumber());
+ R.code->AddInstruction(i0);
+ R.code->AddInstruction(g);
+ R.code->EndBlock(b0->GetRpoNumber());
TestInstr* i1 = TestInstr::New(R.zone(), 102);
TestInstr* g1 = TestInstr::New(R.zone(), 104)->MarkAsControl();
- R.code->StartBlock(b1);
- R.code->AddInstruction(i1, b1);
- R.code->AddInstruction(g1, b1);
- R.code->EndBlock(b1);
+ R.code->StartBlock(b1->GetRpoNumber());
+ R.code->AddInstruction(i1);
+ R.code->AddInstruction(g1);
+ R.code->EndBlock(b1->GetRpoNumber());
CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
@@ -262,10 +277,10 @@
R.allocCode();
TestInstr* i0 = TestInstr::New(R.zone(), 100);
TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
- R.code->StartBlock(b0);
- R.code->AddInstruction(i0, b0);
- R.code->AddInstruction(g, b0);
- R.code->EndBlock(b0);
+ R.code->StartBlock(b0->GetRpoNumber());
+ R.code->AddInstruction(i0);
+ R.code->AddInstruction(g);
+ R.code->EndBlock(b0->GetRpoNumber());
CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
diff --git a/test/cctest/compiler/test-js-constant-cache.cc b/test/cctest/compiler/test-js-constant-cache.cc
index eb0975e..8588f66 100644
--- a/test/cctest/compiler/test-js-constant-cache.cc
+++ b/test/cctest/compiler/test-js-constant-cache.cc
@@ -4,6 +4,7 @@
#include "src/v8.h"
+#include "src/assembler.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-properties-inl.h"
#include "src/compiler/typer.h"
@@ -20,8 +21,8 @@
: main_graph_(zone),
main_common_(zone),
main_javascript_(zone),
- main_typer_(zone),
- main_machine_() {}
+ main_typer_(&main_graph_, MaybeHandle<Context>()),
+ main_machine_(zone) {}
Graph main_graph_;
CommonOperatorBuilder main_common_;
JSOperatorBuilder main_javascript_;
@@ -30,14 +31,19 @@
};
+// TODO(dcarney): JSConstantCacheTester inherits from JSGraph???
class JSConstantCacheTester : public HandleAndZoneScope,
public JSCacheTesterHelper,
public JSGraph {
public:
JSConstantCacheTester()
: JSCacheTesterHelper(main_zone()),
- JSGraph(&main_graph_, &main_common_, &main_javascript_, &main_typer_,
- &main_machine_) {}
+ JSGraph(&main_graph_, &main_common_, &main_javascript_,
+ &main_machine_) {
+ main_graph_.SetStart(main_graph_.NewNode(common()->Start(0)));
+ main_graph_.SetEnd(main_graph_.NewNode(common()->End()));
+ main_typer_.Run();
+ }
Type* upper(Node* node) { return NodeProperties::GetBounds(node).upper; }
@@ -227,7 +233,7 @@
FOR_FLOAT64_INPUTS(i) {
double value = *i;
Node* node = T.Constant(value);
- CHECK(T.upper(node)->Equals(Type::Of(value, T.main_zone())));
+ CHECK(T.upper(node)->Is(Type::Of(value, T.main_zone())));
}
}
@@ -289,3 +295,180 @@
TEST(ExternalReferences) {
// TODO(titzer): test canonicalization of external references.
}
+
+
+static bool Contains(NodeVector* nodes, Node* n) {
+ for (size_t i = 0; i < nodes->size(); i++) {
+ if (nodes->at(i) == n) return true;
+ }
+ return false;
+}
+
+
+static void CheckGetCachedNodesContains(JSConstantCacheTester* T, Node* n) {
+ NodeVector nodes(T->main_zone());
+ T->GetCachedNodes(&nodes);
+ CHECK(Contains(&nodes, n));
+}
+
+
+TEST(JSGraph_GetCachedNodes1) {
+ JSConstantCacheTester T;
+ CheckGetCachedNodesContains(&T, T.TrueConstant());
+ CheckGetCachedNodesContains(&T, T.UndefinedConstant());
+ CheckGetCachedNodesContains(&T, T.TheHoleConstant());
+ CheckGetCachedNodesContains(&T, T.TrueConstant());
+ CheckGetCachedNodesContains(&T, T.FalseConstant());
+ CheckGetCachedNodesContains(&T, T.NullConstant());
+ CheckGetCachedNodesContains(&T, T.ZeroConstant());
+ CheckGetCachedNodesContains(&T, T.OneConstant());
+ CheckGetCachedNodesContains(&T, T.NaNConstant());
+}
+
+
+TEST(JSGraph_GetCachedNodes_int32) {
+ JSConstantCacheTester T;
+
+ int32_t constants[] = {0, 1, 1, 1, 1, 2, 3, 4, 11, 12, 13,
+ 14, 55, -55, -44, -33, -22, -11, 16, 16, 17, 17,
+ 18, 18, 19, 19, 20, 20, 21, 21, 22, 23, 24,
+ 25, 15, 30, 31, 45, 46, 47, 48};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int count_before = T.graph()->NodeCount();
+ NodeVector nodes_before(T.main_zone());
+ T.GetCachedNodes(&nodes_before);
+ Node* n = T.Int32Constant(constants[i]);
+ if (n->id() < count_before) {
+ // An old ID indicates a cached node. It should have been in the set.
+ CHECK(Contains(&nodes_before, n));
+ }
+ // Old or new, it should be in the cached set afterwards.
+ CheckGetCachedNodesContains(&T, n);
+ }
+}
+
+
+TEST(JSGraph_GetCachedNodes_float64) {
+ JSConstantCacheTester T;
+
+ double constants[] = {0, 11.1, 12.2, 13, 14, 55.5, -55.5, -44.4,
+ -33, -22, -11, 0, 11.1, 11.1, 12.3, 12.3,
+ 11, 11, -33.3, -33.3, -22, -11};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int count_before = T.graph()->NodeCount();
+ NodeVector nodes_before(T.main_zone());
+ T.GetCachedNodes(&nodes_before);
+ Node* n = T.Float64Constant(constants[i]);
+ if (n->id() < count_before) {
+ // An old ID indicates a cached node. It should have been in the set.
+ CHECK(Contains(&nodes_before, n));
+ }
+ // Old or new, it should be in the cached set afterwards.
+ CheckGetCachedNodesContains(&T, n);
+ }
+}
+
+
+TEST(JSGraph_GetCachedNodes_int64) {
+ JSConstantCacheTester T;
+
+ int32_t constants[] = {0, 11, 12, 13, 14, 55, -55, -44, -33,
+ -22, -11, 16, 16, 17, 17, 18, 18, 19,
+ 19, 20, 20, 21, 21, 22, 23, 24, 25};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int count_before = T.graph()->NodeCount();
+ NodeVector nodes_before(T.main_zone());
+ T.GetCachedNodes(&nodes_before);
+ Node* n = T.Int64Constant(constants[i]);
+ if (n->id() < count_before) {
+ // An old ID indicates a cached node. It should have been in the set.
+ CHECK(Contains(&nodes_before, n));
+ }
+ // Old or new, it should be in the cached set afterwards.
+ CheckGetCachedNodesContains(&T, n);
+ }
+}
+
+
+TEST(JSGraph_GetCachedNodes_number) {
+ JSConstantCacheTester T;
+
+ double constants[] = {0, 11.1, 12.2, 13, 14, 55.5, -55.5, -44.4,
+ -33, -22, -11, 0, 11.1, 11.1, 12.3, 12.3,
+ 11, 11, -33.3, -33.3, -22, -11};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int count_before = T.graph()->NodeCount();
+ NodeVector nodes_before(T.main_zone());
+ T.GetCachedNodes(&nodes_before);
+ Node* n = T.Constant(constants[i]);
+ if (n->id() < count_before) {
+ // An old ID indicates a cached node. It should have been in the set.
+ CHECK(Contains(&nodes_before, n));
+ }
+ // Old or new, it should be in the cached set afterwards.
+ CheckGetCachedNodesContains(&T, n);
+ }
+}
+
+
+TEST(JSGraph_GetCachedNodes_external) {
+ JSConstantCacheTester T;
+
+ ExternalReference constants[] = {ExternalReference::address_of_min_int(),
+ ExternalReference::address_of_min_int(),
+ ExternalReference::address_of_min_int(),
+ ExternalReference::address_of_one_half(),
+ ExternalReference::address_of_one_half(),
+ ExternalReference::address_of_min_int(),
+ ExternalReference::address_of_the_hole_nan(),
+ ExternalReference::address_of_one_half()};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int count_before = T.graph()->NodeCount();
+ NodeVector nodes_before(T.main_zone());
+ T.GetCachedNodes(&nodes_before);
+ Node* n = T.ExternalConstant(constants[i]);
+ if (n->id() < count_before) {
+ // An old ID indicates a cached node. It should have been in the set.
+ CHECK(Contains(&nodes_before, n));
+ }
+ // Old or new, it should be in the cached set afterwards.
+ CheckGetCachedNodesContains(&T, n);
+ }
+}
+
+
+TEST(JSGraph_GetCachedNodes_together) {
+ JSConstantCacheTester T;
+
+ Node* constants[] = {
+ T.TrueConstant(),
+ T.UndefinedConstant(),
+ T.TheHoleConstant(),
+ T.TrueConstant(),
+ T.FalseConstant(),
+ T.NullConstant(),
+ T.ZeroConstant(),
+ T.OneConstant(),
+ T.NaNConstant(),
+ T.Int32Constant(0),
+ T.Int32Constant(1),
+ T.Int64Constant(-2),
+ T.Int64Constant(-4),
+ T.Float64Constant(0.9),
+ T.Float64Constant(V8_INFINITY),
+ T.Constant(0.99),
+ T.Constant(1.11),
+ T.ExternalConstant(ExternalReference::address_of_one_half())};
+
+ NodeVector nodes(T.main_zone());
+ T.GetCachedNodes(&nodes);
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ CHECK(Contains(&nodes, constants[i]));
+ }
+}
diff --git a/test/cctest/compiler/test-js-context-specialization.cc b/test/cctest/compiler/test-js-context-specialization.cc
index 47c660a..fb7bd94 100644
--- a/test/cctest/compiler/test-js-context-specialization.cc
+++ b/test/cctest/compiler/test-js-context-specialization.cc
@@ -7,7 +7,6 @@
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties-inl.h"
#include "src/compiler/source-position.h"
-#include "src/compiler/typer.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/function-tester.h"
#include "test/cctest/compiler/graph-builder-tester.h"
@@ -22,10 +21,9 @@
: DirectGraphBuilder(new (main_zone()) Graph(main_zone())),
common_(main_zone()),
javascript_(main_zone()),
- machine_(),
+ machine_(main_zone()),
simplified_(main_zone()),
- typer_(main_zone()),
- jsgraph_(graph(), common(), &javascript_, &typer_, &machine_),
+ jsgraph_(graph(), common(), &javascript_, &machine_),
info_(main_isolate(), main_zone()) {}
Factory* factory() { return main_isolate()->factory(); }
@@ -40,7 +38,6 @@
JSOperatorBuilder javascript_;
MachineOperatorBuilder machine_;
SimplifiedOperatorBuilder simplified_;
- Typer typer_;
JSGraph jsgraph_;
CompilationInfo info_;
};
@@ -95,8 +92,8 @@
HeapObjectMatcher<Context> match(new_context_input);
CHECK_EQ(*native, *match.Value().handle());
ContextAccess access = OpParameter<ContextAccess>(r.replacement());
- CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, access.index());
- CHECK_EQ(0, access.depth());
+ CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index()));
+ CHECK_EQ(0, static_cast<int>(access.depth()));
CHECK_EQ(false, access.immutable());
}
@@ -175,8 +172,8 @@
HeapObjectMatcher<Context> match(new_context_input);
CHECK_EQ(*native, *match.Value().handle());
ContextAccess access = OpParameter<ContextAccess>(r.replacement());
- CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, access.index());
- CHECK_EQ(0, access.depth());
+ CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index()));
+ CHECK_EQ(0, static_cast<int>(access.depth()));
CHECK_EQ(false, access.immutable());
}
}
@@ -206,7 +203,7 @@
JSContextSpecializer spec(t.info(), t.jsgraph(), const_context);
{
- // Check that SpecializeToContext() replaces values and forwards effects
+ // Check that specialization replaces values and forwards effects
// correctly, and folds values from constant and non-constant contexts
Node* effect_in = start;
Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true),
@@ -232,8 +229,10 @@
CheckEffectInput(effect_in, load);
CheckEffectInput(load, effect_use);
- // Perform the substitution on the entire graph.
- spec.SpecializeToContext();
+ // Perform the reduction on the entire graph.
+ GraphReducer graph_reducer(t.graph(), t.main_zone());
+ graph_reducer.AddReducer(&spec);
+ graph_reducer.ReduceGraph();
// Effects should have been forwarded (not replaced with a value).
CheckEffectInput(effect_in, effect_use);
diff --git a/test/cctest/compiler/test-js-typed-lowering.cc b/test/cctest/compiler/test-js-typed-lowering.cc
index cf126c2..3023837 100644
--- a/test/cctest/compiler/test-js-typed-lowering.cc
+++ b/test/cctest/compiler/test-js-typed-lowering.cc
@@ -2,14 +2,14 @@
// 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/graph-inl.h"
+#include "src/compiler/js-graph.h"
#include "src/compiler/js-typed-lowering.h"
+#include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties-inl.h"
#include "src/compiler/opcodes.h"
#include "src/compiler/typer.h"
+#include "test/cctest/cctest.h"
using namespace v8::internal;
using namespace v8::internal::compiler;
@@ -21,14 +21,15 @@
binop(NULL),
unop(NULL),
javascript(main_zone()),
+ machine(main_zone()),
simplified(main_zone()),
common(main_zone()),
graph(main_zone()),
- typer(main_zone()),
+ typer(&graph, MaybeHandle<Context>()),
context_node(NULL) {
- typer.DecorateGraph(&graph);
- Node* s = graph.NewNode(common.Start(num_parameters));
- graph.SetStart(s);
+ graph.SetStart(graph.NewNode(common.Start(num_parameters)));
+ graph.SetEnd(graph.NewNode(common.End()));
+ typer.Run();
}
Isolate* isolate;
@@ -49,13 +50,14 @@
}
Node* UndefinedConstant() {
- Unique<Object> unique =
- Unique<Object>::CreateImmovable(isolate->factory()->undefined_value());
+ Unique<HeapObject> unique = Unique<HeapObject>::CreateImmovable(
+ isolate->factory()->undefined_value());
return graph.NewNode(common.HeapConstant(unique));
}
- Node* HeapConstant(Handle<Object> constant) {
- Unique<Object> unique = Unique<Object>::CreateUninitialized(constant);
+ Node* HeapConstant(Handle<HeapObject> constant) {
+ Unique<HeapObject> unique =
+ Unique<HeapObject>::CreateUninitialized(constant);
return graph.NewNode(common.HeapConstant(unique));
}
@@ -65,15 +67,16 @@
Node* stack = graph.NewNode(common.StateValues(0));
Node* state_node =
- graph.NewNode(common.FrameState(JS_FRAME, BailoutId(0), kIgnoreOutput),
+ graph.NewNode(common.FrameState(JS_FRAME, BailoutId(0),
+ OutputFrameStateCombine::Ignore()),
parameters, locals, stack, context, UndefinedConstant());
return state_node;
}
Node* reduce(Node* node) {
- JSGraph jsgraph(&graph, &common, &javascript, &typer, &machine);
- JSTypedLowering reducer(&jsgraph);
+ JSGraph jsgraph(&graph, &common, &javascript, &machine);
+ JSTypedLowering reducer(&jsgraph, main_zone());
Reduction reduction = reducer.Reduce(node);
if (reduction.Changed()) return reduction.replacement();
return node;
@@ -164,17 +167,19 @@
static Type* kInt32Types[] = {
- Type::UnsignedSmall(), Type::OtherSignedSmall(), Type::OtherUnsigned31(),
- Type::OtherUnsigned32(), Type::OtherSigned32(), Type::SignedSmall(),
- Type::Signed32(), Type::Unsigned32(), Type::Integral32()};
+ Type::UnsignedSmall(), Type::NegativeSigned32(),
+ Type::NonNegativeSigned32(), Type::SignedSmall(),
+ Type::Signed32(), Type::Unsigned32(),
+ Type::Integral32()};
static Type* kNumberTypes[] = {
- Type::UnsignedSmall(), Type::OtherSignedSmall(), Type::OtherUnsigned31(),
- Type::OtherUnsigned32(), Type::OtherSigned32(), Type::SignedSmall(),
- Type::Signed32(), Type::Unsigned32(), Type::Integral32(),
- Type::MinusZero(), Type::NaN(), Type::OtherNumber(),
- Type::OrderedNumber(), Type::Number()};
+ Type::UnsignedSmall(), Type::NegativeSigned32(),
+ Type::NonNegativeSigned32(), Type::SignedSmall(),
+ Type::Signed32(), Type::Unsigned32(),
+ Type::Integral32(), Type::MinusZero(),
+ Type::NaN(), Type::OrderedNumber(),
+ Type::PlainNumber(), Type::Number()};
static Type* kJSTypes[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
@@ -260,16 +265,15 @@
static void CheckToI32(Node* old_input, Node* new_input, bool is_signed) {
Type* old_type = NodeProperties::GetBounds(old_input).upper;
+ Type* new_type = NodeProperties::GetBounds(new_input).upper;
Type* expected_type = I32Type(is_signed);
+ CHECK(new_type->Is(expected_type));
if (old_type->Is(expected_type)) {
CHECK_EQ(old_input, new_input);
} else if (new_input->opcode() == IrOpcode::kNumberConstant) {
- CHECK(NodeProperties::GetBounds(new_input).upper->Is(expected_type));
double v = OpParameter<double>(new_input);
double e = static_cast<double>(is_signed ? FastD2I(v) : FastD2UI(v));
CHECK_EQ(e, v);
- } else {
- CHECK_EQ(NumberToI32(is_signed), new_input->opcode());
}
}
@@ -302,12 +306,13 @@
TEST(Int32BitwiseShifts) {
JSBitwiseShiftTypedLoweringTester R;
- Type* types[] = {
- Type::SignedSmall(), Type::UnsignedSmall(), Type::OtherSigned32(),
- Type::Unsigned32(), Type::Signed32(), Type::MinusZero(),
- Type::NaN(), Type::OtherNumber(), Type::Undefined(),
- Type::Null(), Type::Boolean(), Type::Number(),
- Type::String(), Type::Object()};
+ Type* types[] = {Type::SignedSmall(), Type::UnsignedSmall(),
+ Type::NegativeSigned32(), Type::NonNegativeSigned32(),
+ Type::Unsigned32(), Type::Signed32(),
+ Type::MinusZero(), Type::NaN(),
+ Type::Undefined(), Type::Null(),
+ Type::Boolean(), Type::Number(),
+ Type::PlainNumber(), Type::String()};
for (size_t i = 0; i < arraysize(types); ++i) {
Node* p0 = R.Parameter(types[i], 0);
@@ -325,9 +330,13 @@
CheckToI32(p0, r0, R.signedness[k]);
- R.CheckPureBinop(IrOpcode::kWord32And, r1);
- CheckToI32(p1, r1->InputAt(0), R.signedness[k + 1]);
- R.CheckInt32Constant(0x1F, r1->InputAt(1));
+ if (r1->opcode() == IrOpcode::kWord32And) {
+ R.CheckPureBinop(IrOpcode::kWord32And, r1);
+ CheckToI32(p1, r1->InputAt(0), R.signedness[k + 1]);
+ R.CheckInt32Constant(0x1F, r1->InputAt(1));
+ } else {
+ CheckToI32(p1, r1, R.signedness[k]);
+ }
}
}
}
@@ -363,11 +372,11 @@
JSBitwiseTypedLoweringTester R;
Type* types[] = {
- Type::SignedSmall(), Type::UnsignedSmall(), Type::OtherSigned32(),
- Type::Unsigned32(), Type::Signed32(), Type::MinusZero(),
- Type::NaN(), Type::OtherNumber(), Type::Undefined(),
- Type::Null(), Type::Boolean(), Type::Number(),
- Type::String(), Type::Object()};
+ Type::SignedSmall(), Type::UnsignedSmall(), Type::Unsigned32(),
+ Type::Signed32(), Type::MinusZero(), Type::NaN(),
+ Type::OrderedNumber(), Type::PlainNumber(), Type::Undefined(),
+ Type::Null(), Type::Boolean(), Type::Number(),
+ Type::String()};
for (size_t i = 0; i < arraysize(types); ++i) {
Node* p0 = R.Parameter(types[i], 0);
@@ -498,20 +507,6 @@
CHECK_EQ(IrOpcode::kParameter, r->opcode());
}
- { // ToBoolean(ordered-number)
- Node* r = R.ReduceUnop(op, Type::OrderedNumber());
- CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
- Node* i = r->InputAt(0);
- CHECK_EQ(IrOpcode::kNumberEqual, i->opcode());
- // ToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x, #0))
- }
-
- { // ToBoolean(string)
- Node* r = R.ReduceUnop(op, Type::String());
- // TODO(titzer): test will break with better js-typed-lowering
- CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
- }
-
{ // ToBoolean(object)
Node* r = R.ReduceUnop(op, Type::DetectableObject());
R.CheckTrue(r);
@@ -524,39 +519,7 @@
{ // ToBoolean(object)
Node* r = R.ReduceUnop(op, Type::Object());
- CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
- }
-}
-
-
-TEST(JSToBoolean_replacement) {
- JSTypedLoweringTester R;
-
- Type* types[] = {Type::Null(), Type::Undefined(),
- Type::Boolean(), Type::OrderedNumber(),
- Type::DetectableObject(), Type::Undetectable()};
-
- for (size_t i = 0; i < arraysize(types); i++) {
- Node* n = R.Parameter(types[i]);
- Node* c = R.graph.NewNode(R.javascript.ToBoolean(), n, R.context(),
- R.start(), R.start());
- Node* effect_use = R.UseForEffect(c);
- Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
-
- R.CheckEffectInput(c, effect_use);
- Node* r = R.reduce(c);
-
- if (types[i]->Is(Type::Boolean())) {
- CHECK_EQ(n, r);
- } else if (types[i]->Is(Type::OrderedNumber())) {
- CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
- } else {
- CHECK_EQ(IrOpcode::kHeapConstant, r->opcode());
- }
-
- CHECK_EQ(n, add->InputAt(0));
- CHECK_EQ(r, add->InputAt(1));
- R.CheckEffectInput(R.start(), effect_use);
+ CHECK_EQ(IrOpcode::kAnyToBoolean, r->opcode());
}
}
@@ -690,33 +653,22 @@
R.javascript.GreaterThan(), R.simplified.NumberLessThan(),
R.javascript.GreaterThanOrEqual(), R.simplified.NumberLessThanOrEqual()};
- for (size_t i = 0; i < arraysize(kJSTypes); i++) {
- Type* t0 = kJSTypes[i];
- // Skip Type::String and Type::Receiver which might coerce into a string.
- if (t0->Is(Type::String()) || t0->Is(Type::Receiver())) continue;
- Node* p0 = R.Parameter(t0, 0);
+ Node* const p0 = R.Parameter(Type::Number(), 0);
+ Node* const p1 = R.Parameter(Type::Number(), 1);
- for (size_t j = 0; j < arraysize(kJSTypes); j++) {
- Type* t1 = kJSTypes[j];
- // Skip Type::String and Type::Receiver which might coerce into a string.
- if (t1->Is(Type::String()) || t0->Is(Type::Receiver())) continue;
- Node* p1 = R.Parameter(t1, 1);
+ for (size_t k = 0; k < arraysize(ops); k += 2) {
+ Node* cmp = R.Binop(ops[k], p0, p1);
+ Node* r = R.reduce(cmp);
- for (size_t k = 0; k < arraysize(ops); k += 2) {
- Node* cmp = R.Binop(ops[k], p0, p1);
- Node* r = R.reduce(cmp);
-
- R.CheckPureBinop(ops[k + 1], r);
- if (k >= 4) {
- // GreaterThan and GreaterThanOrEqual commute the inputs
- // and use the LessThan and LessThanOrEqual operators.
- CheckIsConvertedToNumber(p1, r->InputAt(0));
- CheckIsConvertedToNumber(p0, r->InputAt(1));
- } else {
- CheckIsConvertedToNumber(p0, r->InputAt(0));
- CheckIsConvertedToNumber(p1, r->InputAt(1));
- }
- }
+ R.CheckPureBinop(ops[k + 1], r);
+ if (k >= 4) {
+ // GreaterThan and GreaterThanOrEqual commute the inputs
+ // and use the LessThan and LessThanOrEqual operators.
+ CheckIsConvertedToNumber(p1, r->InputAt(0));
+ CheckIsConvertedToNumber(p0, r->InputAt(1));
+ } else {
+ CheckIsConvertedToNumber(p0, r->InputAt(0));
+ CheckIsConvertedToNumber(p1, r->InputAt(1));
}
}
}
@@ -753,51 +705,18 @@
}
-TEST(ObjectComparison) {
- JSTypedLoweringTester R;
-
- Node* p0 = R.Parameter(Type::Number(), 0);
- Node* p1 = R.Parameter(Type::Object(), 1);
-
- Node* cmp = R.Binop(R.javascript.LessThan(), p0, p1);
- Node* effect_use = R.UseForEffect(cmp);
-
- R.CheckEffectInput(R.start(), cmp);
- R.CheckEffectInput(cmp, effect_use);
-
- Node* r = R.reduce(cmp);
-
- R.CheckPureBinop(R.simplified.NumberLessThan(), r);
-
- Node* i0 = r->InputAt(0);
- Node* i1 = r->InputAt(1);
-
- CHECK_EQ(p0, i0);
- CHECK_NE(p1, i1);
- CHECK_EQ(IrOpcode::kParameter, i0->opcode());
- CHECK_EQ(IrOpcode::kJSToNumber, i1->opcode());
-
- // Check effect chain is correct.
- R.CheckEffectInput(R.start(), i1);
- R.CheckEffectInput(i1, effect_use);
-}
-
-
TEST(UnaryNot) {
JSTypedLoweringTester R;
const Operator* opnot = R.javascript.UnaryNot();
for (size_t i = 0; i < arraysize(kJSTypes); i++) {
Node* orig = R.Unop(opnot, R.Parameter(kJSTypes[i]));
- Node* use = R.graph.NewNode(R.common.Return(), orig);
Node* r = R.reduce(orig);
- // TODO(titzer): test will break if/when js-typed-lowering constant folds.
- CHECK_EQ(IrOpcode::kBooleanNot, use->InputAt(0)->opcode());
if (r == orig && orig->opcode() == IrOpcode::kJSToBoolean) {
// The original node was turned into a ToBoolean.
CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
- } else {
+ } else if (r->opcode() != IrOpcode::kHeapConstant) {
CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
}
}
@@ -852,7 +771,7 @@
if (effect_use != NULL) {
R.CheckEffectInput(R.start(), effect_use);
// Check that value uses of ToNumber() do not go to start().
- for (int i = 0; i < effect_use->op()->InputCount(); i++) {
+ for (int i = 0; i < effect_use->op()->ValueInputCount(); i++) {
CHECK_NE(R.start(), effect_use->InputAt(i));
}
}
@@ -904,9 +823,9 @@
Node* CheckConverted(IrOpcode::Value opcode, Node* node, bool effects) {
CHECK_EQ(opcode, node->opcode());
if (effects) {
- CHECK_LT(0, OperatorProperties::GetEffectInputCount(node->op()));
+ CHECK_LT(0, node->op()->EffectInputCount());
} else {
- CHECK_EQ(0, OperatorProperties::GetEffectInputCount(node->op()));
+ CHECK_EQ(0, node->op()->EffectInputCount());
}
return node;
}
@@ -1030,7 +949,7 @@
};
for (size_t j = 0; j < arraysize(ops); j += 2) {
- BinopEffectsTester B(ops[j], Type::Object(), Type::String());
+ BinopEffectsTester B(ops[j], Type::Symbol(), Type::Symbol());
CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
@@ -1103,8 +1022,8 @@
CHECK_EQ(B.p1, i0->InputAt(0));
CHECK_EQ(B.p0, i1->InputAt(0));
- // But effects should be ordered start -> i1 -> i0 -> effect_use
- B.CheckEffectOrdering(i1, i0);
+ // But effects should be ordered start -> i1 -> effect_use
+ B.CheckEffectOrdering(i1);
}
for (size_t j = 0; j < arraysize(ops); j += 2) {
@@ -1166,7 +1085,7 @@
for (int j = 0; j < R.kNumberOps; j += 2) {
bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
- BinopEffectsTester B(R.ops[j], Type::Number(), Type::Object());
+ BinopEffectsTester B(R.ops[j], Type::Number(), Type::Primitive());
B.R.CheckPureBinop(B.result->opcode(), B.result);
@@ -1183,7 +1102,7 @@
for (int j = 0; j < R.kNumberOps; j += 2) {
bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
- BinopEffectsTester B(R.ops[j], Type::Object(), Type::Number());
+ BinopEffectsTester B(R.ops[j], Type::Primitive(), Type::Number());
B.R.CheckPureBinop(B.result->opcode(), B.result);
@@ -1200,7 +1119,7 @@
for (int j = 0; j < R.kNumberOps; j += 2) {
bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
- BinopEffectsTester B(R.ops[j], Type::Object(), Type::Object());
+ BinopEffectsTester B(R.ops[j], Type::Primitive(), Type::Primitive());
B.R.CheckPureBinop(B.result->opcode(), B.result);
@@ -1218,33 +1137,6 @@
}
-TEST(UnaryNotEffects) {
- JSTypedLoweringTester R;
- const Operator* opnot = R.javascript.UnaryNot();
-
- for (size_t i = 0; i < arraysize(kJSTypes); i++) {
- Node* p0 = R.Parameter(kJSTypes[i], 0);
- Node* orig = R.Unop(opnot, p0);
- Node* effect_use = R.UseForEffect(orig);
- Node* value_use = R.graph.NewNode(R.common.Return(), orig);
- Node* r = R.reduce(orig);
- // TODO(titzer): test will break if/when js-typed-lowering constant folds.
- CHECK_EQ(IrOpcode::kBooleanNot, value_use->InputAt(0)->opcode());
-
- if (r == orig && orig->opcode() == IrOpcode::kJSToBoolean) {
- // The original node was turned into a ToBoolean, which has an effect.
- CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
- R.CheckEffectInput(R.start(), orig);
- R.CheckEffectInput(orig, effect_use);
- } else {
- // effect should have been removed from this node.
- CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
- R.CheckEffectInput(R.start(), effect_use);
- }
- }
-}
-
-
TEST(Int32AddNarrowing) {
{
JSBitwiseTypedLoweringTester R;
@@ -1263,11 +1155,7 @@
Node* r = R.reduce(or_node);
CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
- CHECK_EQ(IrOpcode::kInt32Add, add_node->opcode());
- bool is_signed = l ? R.signedness[o] : R.signedness[o + 1];
-
- Type* add_type = NodeProperties::GetBounds(add_node).upper;
- CHECK(add_type->Is(I32Type(is_signed)));
+ CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
}
}
}
@@ -1290,40 +1178,33 @@
Node* r = R.reduce(or_node);
CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
- CHECK_EQ(IrOpcode::kInt32Add, add_node->opcode());
- bool is_signed = l ? R.signedness[o] : R.signedness[o + 1];
-
- Type* add_type = NodeProperties::GetBounds(add_node).upper;
- CHECK(add_type->Is(I32Type(is_signed)));
+ CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
}
}
}
}
}
-}
+ {
+ JSBitwiseTypedLoweringTester R;
+ for (int o = 0; o < R.kNumberOps; o += 2) {
+ Node* n0 = R.Parameter(I32Type(R.signedness[o]));
+ Node* n1 = R.Parameter(I32Type(R.signedness[o + 1]));
+ Node* one = R.graph.NewNode(R.common.NumberConstant(1));
-TEST(Int32AddNarrowingNotOwned) {
- JSBitwiseTypedLoweringTester R;
-
- for (int o = 0; o < R.kNumberOps; o += 2) {
- Node* n0 = R.Parameter(I32Type(R.signedness[o]));
- Node* n1 = R.Parameter(I32Type(R.signedness[o + 1]));
- Node* one = R.graph.NewNode(R.common.NumberConstant(1));
-
- Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
- Node* or_node = R.Binop(R.ops[o], add_node, one);
- Node* other_use = R.Binop(R.simplified.NumberAdd(), add_node, one);
- Node* r = R.reduce(or_node);
- CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
- // Should not be reduced to Int32Add because of the other number add.
- CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
- // Conversion to int32 should be done.
- CheckToI32(add_node, r->InputAt(0), R.signedness[o]);
- CheckToI32(one, r->InputAt(1), R.signedness[o + 1]);
- // The other use should also not be touched.
- CHECK_EQ(add_node, other_use->InputAt(0));
- CHECK_EQ(one, other_use->InputAt(1));
+ Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
+ Node* or_node = R.Binop(R.ops[o], add_node, one);
+ Node* other_use = R.Binop(R.simplified.NumberAdd(), add_node, one);
+ Node* r = R.reduce(or_node);
+ CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
+ CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
+ // Conversion to int32 should be done.
+ CheckToI32(add_node, r->InputAt(0), R.signedness[o]);
+ CheckToI32(one, r->InputAt(1), R.signedness[o + 1]);
+ // The other use should also not be touched.
+ CHECK_EQ(add_node, other_use->InputAt(0));
+ CHECK_EQ(one, other_use->InputAt(1));
+ }
}
}
diff --git a/test/cctest/compiler/test-jump-threading.cc b/test/cctest/compiler/test-jump-threading.cc
new file mode 100644
index 0000000..74bf43d
--- /dev/null
+++ b/test/cctest/compiler/test-jump-threading.cc
@@ -0,0 +1,764 @@
+// 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/instruction.h"
+#include "src/compiler/instruction-codes.h"
+#include "src/compiler/jump-threading.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+typedef BasicBlock::RpoNumber RpoNumber;
+
+class TestCode : public HandleAndZoneScope {
+ public:
+ TestCode()
+ : HandleAndZoneScope(),
+ blocks_(main_zone()),
+ sequence_(main_zone(), &blocks_),
+ rpo_number_(RpoNumber::FromInt(0)),
+ current_(NULL) {}
+
+ ZoneVector<InstructionBlock*> blocks_;
+ InstructionSequence sequence_;
+ RpoNumber rpo_number_;
+ InstructionBlock* current_;
+
+ int Jump(int target) {
+ Start();
+ InstructionOperand* ops[] = {UseRpo(target)};
+ sequence_.AddInstruction(Instruction::New(main_zone(), kArchJmp, 0, NULL, 1,
+ ops, 0, NULL)->MarkAsControl());
+ int pos = static_cast<int>(sequence_.instructions().size() - 1);
+ End();
+ return pos;
+ }
+ void Fallthru() {
+ Start();
+ End();
+ }
+ int Branch(int ttarget, int ftarget) {
+ Start();
+ InstructionOperand* ops[] = {UseRpo(ttarget), UseRpo(ftarget)};
+ InstructionCode code = 119 | FlagsModeField::encode(kFlags_branch) |
+ FlagsConditionField::encode(kEqual);
+ sequence_.AddInstruction(Instruction::New(main_zone(), code, 0, NULL, 2,
+ ops, 0, NULL)->MarkAsControl());
+ int pos = static_cast<int>(sequence_.instructions().size() - 1);
+ End();
+ return pos;
+ }
+ void Nop() {
+ Start();
+ sequence_.AddInstruction(Instruction::New(main_zone(), kArchNop));
+ }
+ void RedundantMoves() {
+ Start();
+ sequence_.AddInstruction(Instruction::New(main_zone(), kArchNop));
+ int index = static_cast<int>(sequence_.instructions().size()) - 1;
+ sequence_.AddGapMove(index, RegisterOperand::Create(13, main_zone()),
+ RegisterOperand::Create(13, main_zone()));
+ }
+ void NonRedundantMoves() {
+ Start();
+ sequence_.AddInstruction(Instruction::New(main_zone(), kArchNop));
+ int index = static_cast<int>(sequence_.instructions().size()) - 1;
+ sequence_.AddGapMove(index, ImmediateOperand::Create(11, main_zone()),
+ RegisterOperand::Create(11, main_zone()));
+ }
+ void Other() {
+ Start();
+ sequence_.AddInstruction(Instruction::New(main_zone(), 155));
+ }
+ void End() {
+ Start();
+ sequence_.EndBlock(current_->rpo_number());
+ current_ = NULL;
+ rpo_number_ = RpoNumber::FromInt(rpo_number_.ToInt() + 1);
+ }
+ InstructionOperand* UseRpo(int num) {
+ int index = sequence_.AddImmediate(Constant(RpoNumber::FromInt(num)));
+ return ImmediateOperand::Create(index, main_zone());
+ }
+ void Start(bool deferred = false) {
+ if (current_ == NULL) {
+ current_ = new (main_zone()) InstructionBlock(
+ main_zone(), BasicBlock::Id::FromInt(rpo_number_.ToInt()),
+ rpo_number_, RpoNumber::Invalid(), RpoNumber::Invalid(), deferred);
+ blocks_.push_back(current_);
+ sequence_.StartBlock(rpo_number_);
+ }
+ }
+ void Defer() {
+ CHECK(current_ == NULL);
+ Start(true);
+ }
+};
+
+
+void VerifyForwarding(TestCode& code, int count, int* expected) {
+ Zone local_zone(code.main_isolate());
+ ZoneVector<RpoNumber> result(&local_zone);
+ JumpThreading::ComputeForwarding(&local_zone, result, &code.sequence_);
+
+ CHECK(count == static_cast<int>(result.size()));
+ for (int i = 0; i < count; i++) {
+ CHECK(expected[i] == result[i].ToInt());
+ }
+}
+
+
+TEST(FwEmpty1) {
+ TestCode code;
+
+ // B0
+ code.Jump(1);
+ // B1
+ code.Jump(2);
+ // B2
+ code.End();
+
+ static int expected[] = {2, 2, 2};
+ VerifyForwarding(code, 3, expected);
+}
+
+
+TEST(FwEmptyN) {
+ for (int i = 0; i < 9; i++) {
+ TestCode code;
+
+ // B0
+ code.Jump(1);
+ // B1
+ for (int j = 0; j < i; j++) code.Nop();
+ code.Jump(2);
+ // B2
+ code.End();
+
+ static int expected[] = {2, 2, 2};
+ VerifyForwarding(code, 3, expected);
+ }
+}
+
+
+TEST(FwNone1) {
+ TestCode code;
+
+ // B0
+ code.End();
+
+ static int expected[] = {0};
+ VerifyForwarding(code, 1, expected);
+}
+
+
+TEST(FwMoves1) {
+ TestCode code;
+
+ // B0
+ code.RedundantMoves();
+ code.End();
+
+ static int expected[] = {0};
+ VerifyForwarding(code, 1, expected);
+}
+
+
+TEST(FwMoves2) {
+ TestCode code;
+
+ // B0
+ code.RedundantMoves();
+ code.Fallthru();
+ // B1
+ code.End();
+
+ static int expected[] = {1, 1};
+ VerifyForwarding(code, 2, expected);
+}
+
+
+TEST(FwMoves2b) {
+ TestCode code;
+
+ // B0
+ code.NonRedundantMoves();
+ code.Fallthru();
+ // B1
+ code.End();
+
+ static int expected[] = {0, 1};
+ VerifyForwarding(code, 2, expected);
+}
+
+
+TEST(FwOther2) {
+ TestCode code;
+
+ // B0
+ code.Other();
+ code.Fallthru();
+ // B1
+ code.End();
+
+ static int expected[] = {0, 1};
+ VerifyForwarding(code, 2, expected);
+}
+
+
+TEST(FwNone2a) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.End();
+
+ static int expected[] = {1, 1};
+ VerifyForwarding(code, 2, expected);
+}
+
+
+TEST(FwNone2b) {
+ TestCode code;
+
+ // B0
+ code.Jump(1);
+ // B1
+ code.End();
+
+ static int expected[] = {1, 1};
+ VerifyForwarding(code, 2, expected);
+}
+
+
+TEST(FwLoop1) {
+ TestCode code;
+
+ // B0
+ code.Jump(0);
+
+ static int expected[] = {0};
+ VerifyForwarding(code, 1, expected);
+}
+
+
+TEST(FwLoop2) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Jump(0);
+
+ static int expected[] = {0, 0};
+ VerifyForwarding(code, 2, expected);
+}
+
+
+TEST(FwLoop3) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Jump(0);
+
+ static int expected[] = {0, 0, 0};
+ VerifyForwarding(code, 3, expected);
+}
+
+
+TEST(FwLoop1b) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Jump(1);
+
+ static int expected[] = {1, 1};
+ VerifyForwarding(code, 2, expected);
+}
+
+
+TEST(FwLoop2b) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Jump(1);
+
+ static int expected[] = {1, 1, 1};
+ VerifyForwarding(code, 3, expected);
+}
+
+
+TEST(FwLoop3b) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Fallthru();
+ // B3
+ code.Jump(1);
+
+ static int expected[] = {1, 1, 1, 1};
+ VerifyForwarding(code, 4, expected);
+}
+
+
+TEST(FwLoop2_1a) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Fallthru();
+ // B3
+ code.Jump(1);
+ // B4
+ code.Jump(2);
+
+ static int expected[] = {1, 1, 1, 1, 1};
+ VerifyForwarding(code, 5, expected);
+}
+
+
+TEST(FwLoop2_1b) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Jump(4);
+ // B3
+ code.Jump(1);
+ // B4
+ code.Jump(2);
+
+ static int expected[] = {2, 2, 2, 2, 2};
+ VerifyForwarding(code, 5, expected);
+}
+
+
+TEST(FwLoop2_1c) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Jump(4);
+ // B3
+ code.Jump(2);
+ // B4
+ code.Jump(1);
+
+ static int expected[] = {1, 1, 1, 1, 1};
+ VerifyForwarding(code, 5, expected);
+}
+
+
+TEST(FwLoop2_1d) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Jump(1);
+ // B3
+ code.Jump(1);
+ // B4
+ code.Jump(1);
+
+ static int expected[] = {1, 1, 1, 1, 1};
+ VerifyForwarding(code, 5, expected);
+}
+
+
+TEST(FwLoop3_1a) {
+ TestCode code;
+
+ // B0
+ code.Fallthru();
+ // B1
+ code.Fallthru();
+ // B2
+ code.Fallthru();
+ // B3
+ code.Jump(2);
+ // B4
+ code.Jump(1);
+ // B5
+ code.Jump(0);
+
+ static int expected[] = {2, 2, 2, 2, 2, 2};
+ VerifyForwarding(code, 6, expected);
+}
+
+
+TEST(FwDiamonds) {
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ TestCode code;
+ // B0
+ code.Branch(1, 2);
+ // B1
+ if (i) code.Other();
+ code.Jump(3);
+ // B2
+ if (j) code.Other();
+ code.Jump(3);
+ // B3
+ code.End();
+
+ int expected[] = {0, i ? 1 : 3, j ? 2 : 3, 3};
+ VerifyForwarding(code, 4, expected);
+ }
+ }
+}
+
+
+TEST(FwDiamonds2) {
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int k = 0; k < 2; k++) {
+ TestCode code;
+ // B0
+ code.Branch(1, 2);
+ // B1
+ if (i) code.Other();
+ code.Jump(3);
+ // B2
+ if (j) code.Other();
+ code.Jump(3);
+ // B3
+ if (k) code.NonRedundantMoves();
+ code.Jump(4);
+ // B4
+ code.End();
+
+ int merge = k ? 3 : 4;
+ int expected[] = {0, i ? 1 : merge, j ? 2 : merge, merge, 4};
+ VerifyForwarding(code, 5, expected);
+ }
+ }
+ }
+}
+
+
+TEST(FwDoubleDiamonds) {
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int x = 0; x < 2; x++) {
+ for (int y = 0; y < 2; y++) {
+ TestCode code;
+ // B0
+ code.Branch(1, 2);
+ // B1
+ if (i) code.Other();
+ code.Jump(3);
+ // B2
+ if (j) code.Other();
+ code.Jump(3);
+ // B3
+ code.Branch(4, 5);
+ // B4
+ if (x) code.Other();
+ code.Jump(6);
+ // B5
+ if (y) code.Other();
+ code.Jump(6);
+ // B6
+ code.End();
+
+ int expected[] = {0, i ? 1 : 3, j ? 2 : 3, 3,
+ x ? 4 : 6, y ? 5 : 6, 6};
+ VerifyForwarding(code, 7, expected);
+ }
+ }
+ }
+ }
+}
+
+template <int kSize>
+void RunPermutationsRecursive(int outer[kSize], int start,
+ void (*run)(int*, int)) {
+ int permutation[kSize];
+
+ for (int i = 0; i < kSize; i++) permutation[i] = outer[i];
+
+ int count = kSize - start;
+ if (count == 0) return run(permutation, kSize);
+ for (int i = start; i < kSize; i++) {
+ permutation[start] = outer[i];
+ permutation[i] = outer[start];
+ RunPermutationsRecursive<kSize>(permutation, start + 1, run);
+ permutation[i] = outer[i];
+ permutation[start] = outer[start];
+ }
+}
+
+
+template <int kSize>
+void RunAllPermutations(void (*run)(int*, int)) {
+ int permutation[kSize];
+ for (int i = 0; i < kSize; i++) permutation[i] = i;
+ RunPermutationsRecursive<kSize>(permutation, 0, run);
+}
+
+
+void PrintPermutation(int* permutation, int size) {
+ printf("{ ");
+ for (int i = 0; i < size; i++) {
+ if (i > 0) printf(", ");
+ printf("%d", permutation[i]);
+ }
+ printf(" }\n");
+}
+
+
+int find(int x, int* permutation, int size) {
+ for (int i = 0; i < size; i++) {
+ if (permutation[i] == x) return i;
+ }
+ return size;
+}
+
+
+void RunPermutedChain(int* permutation, int size) {
+ TestCode code;
+ int cur = -1;
+ for (int i = 0; i < size; i++) {
+ code.Jump(find(cur + 1, permutation, size) + 1);
+ cur = permutation[i];
+ }
+ code.Jump(find(cur + 1, permutation, size) + 1);
+ code.End();
+
+ int expected[] = {size + 1, size + 1, size + 1, size + 1,
+ size + 1, size + 1, size + 1};
+ VerifyForwarding(code, size + 2, expected);
+}
+
+
+TEST(FwPermuted_chain) {
+ RunAllPermutations<3>(RunPermutedChain);
+ RunAllPermutations<4>(RunPermutedChain);
+ RunAllPermutations<5>(RunPermutedChain);
+}
+
+
+void RunPermutedDiamond(int* permutation, int size) {
+ TestCode code;
+ int br = 1 + find(0, permutation, size);
+ code.Jump(br);
+ for (int i = 0; i < size; i++) {
+ switch (permutation[i]) {
+ case 0:
+ code.Branch(1 + find(1, permutation, size),
+ 1 + find(2, permutation, size));
+ break;
+ case 1:
+ code.Jump(1 + find(3, permutation, size));
+ break;
+ case 2:
+ code.Jump(1 + find(3, permutation, size));
+ break;
+ case 3:
+ code.Jump(5);
+ break;
+ }
+ }
+ code.End();
+
+ int expected[] = {br, 5, 5, 5, 5, 5};
+ expected[br] = br;
+ VerifyForwarding(code, 6, expected);
+}
+
+
+TEST(FwPermuted_diamond) { RunAllPermutations<4>(RunPermutedDiamond); }
+
+
+void ApplyForwarding(TestCode& code, int size, int* forward) {
+ ZoneVector<RpoNumber> vector(code.main_zone());
+ for (int i = 0; i < size; i++) {
+ vector.push_back(RpoNumber::FromInt(forward[i]));
+ }
+ JumpThreading::ApplyForwarding(vector, &code.sequence_);
+}
+
+
+void CheckJump(TestCode& code, int pos, int target) {
+ Instruction* instr = code.sequence_.InstructionAt(pos);
+ CHECK_EQ(kArchJmp, instr->arch_opcode());
+ CHECK_EQ(1, static_cast<int>(instr->InputCount()));
+ CHECK_EQ(0, static_cast<int>(instr->OutputCount()));
+ CHECK_EQ(0, static_cast<int>(instr->TempCount()));
+ CHECK_EQ(target, code.sequence_.InputRpo(instr, 0).ToInt());
+}
+
+
+void CheckNop(TestCode& code, int pos) {
+ Instruction* instr = code.sequence_.InstructionAt(pos);
+ CHECK_EQ(kArchNop, instr->arch_opcode());
+ CHECK_EQ(0, static_cast<int>(instr->InputCount()));
+ CHECK_EQ(0, static_cast<int>(instr->OutputCount()));
+ CHECK_EQ(0, static_cast<int>(instr->TempCount()));
+}
+
+
+void CheckBranch(TestCode& code, int pos, int t1, int t2) {
+ Instruction* instr = code.sequence_.InstructionAt(pos);
+ CHECK_EQ(2, static_cast<int>(instr->InputCount()));
+ CHECK_EQ(0, static_cast<int>(instr->OutputCount()));
+ CHECK_EQ(0, static_cast<int>(instr->TempCount()));
+ CHECK_EQ(t1, code.sequence_.InputRpo(instr, 0).ToInt());
+ CHECK_EQ(t2, code.sequence_.InputRpo(instr, 1).ToInt());
+}
+
+
+void CheckAssemblyOrder(TestCode& code, int size, int* expected) {
+ int i = 0;
+ for (auto const block : code.sequence_.instruction_blocks()) {
+ CHECK_EQ(expected[i++], block->ao_number().ToInt());
+ }
+}
+
+
+TEST(Rewire1) {
+ TestCode code;
+
+ // B0
+ int j1 = code.Jump(1);
+ // B1
+ int j2 = code.Jump(2);
+ // B2
+ code.End();
+
+ static int forward[] = {2, 2, 2};
+ ApplyForwarding(code, 3, forward);
+ CheckJump(code, j1, 2);
+ CheckNop(code, j2);
+
+ static int assembly[] = {0, 1, 1};
+ CheckAssemblyOrder(code, 3, assembly);
+}
+
+
+TEST(Rewire1_deferred) {
+ TestCode code;
+
+ // B0
+ int j1 = code.Jump(1);
+ // B1
+ int j2 = code.Jump(2);
+ // B2
+ code.Defer();
+ int j3 = code.Jump(3);
+ // B3
+ code.End();
+
+ static int forward[] = {3, 3, 3, 3};
+ ApplyForwarding(code, 4, forward);
+ CheckJump(code, j1, 3);
+ CheckNop(code, j2);
+ CheckNop(code, j3);
+
+ static int assembly[] = {0, 1, 2, 1};
+ CheckAssemblyOrder(code, 4, assembly);
+}
+
+
+TEST(Rewire2_deferred) {
+ TestCode code;
+
+ // B0
+ code.Other();
+ int j1 = code.Jump(1);
+ // B1
+ code.Defer();
+ code.Fallthru();
+ // B2
+ code.Defer();
+ int j2 = code.Jump(3);
+ // B3
+ code.End();
+
+ static int forward[] = {0, 1, 2, 3};
+ ApplyForwarding(code, 4, forward);
+ CheckJump(code, j1, 1);
+ CheckJump(code, j2, 3);
+
+ static int assembly[] = {0, 2, 3, 1};
+ CheckAssemblyOrder(code, 4, assembly);
+}
+
+
+TEST(Rewire_diamond) {
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ TestCode code;
+ // B0
+ int j1 = code.Jump(1);
+ // B1
+ int b1 = code.Branch(2, 3);
+ // B2
+ int j2 = code.Jump(4);
+ // B3
+ int j3 = code.Jump(4);
+ // B5
+ code.End();
+
+ int forward[] = {0, 1, i ? 4 : 2, j ? 4 : 3, 4};
+ ApplyForwarding(code, 5, forward);
+ CheckJump(code, j1, 1);
+ CheckBranch(code, b1, i ? 4 : 2, j ? 4 : 3);
+ if (i) {
+ CheckNop(code, j2);
+ } else {
+ CheckJump(code, j2, 4);
+ }
+ if (j) {
+ CheckNop(code, j3);
+ } else {
+ CheckJump(code, j3, 4);
+ }
+
+ int assembly[] = {0, 1, 2, 3, 4};
+ if (i) {
+ for (int k = 3; k < 5; k++) assembly[k]--;
+ }
+ if (j) {
+ for (int k = 4; k < 5; k++) assembly[k]--;
+ }
+ CheckAssemblyOrder(code, 5, assembly);
+ }
+ }
+}
+
+} // namespace compiler
+} // namespace internal
+} // namespace v8
diff --git a/test/cctest/compiler/test-linkage.cc b/test/cctest/compiler/test-linkage.cc
index ff65d6e..117caf2 100644
--- a/test/cctest/compiler/test-linkage.cc
+++ b/test/cctest/compiler/test-linkage.cc
@@ -8,7 +8,6 @@
#include "src/zone.h"
#include "src/compiler/common-operator.h"
-#include "src/compiler/generic-node-inl.h"
#include "src/compiler/graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/machine-operator.h"
@@ -23,8 +22,8 @@
using namespace v8::internal;
using namespace v8::internal::compiler;
-static SimpleOperator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
- 0, 0, "dummy");
+static Operator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
+ "dummy", 0, 0, 0, 0, 0, 0);
// So we can get a real JS function.
static Handle<JSFunction> Compile(const char* source) {
@@ -45,7 +44,7 @@
InitializedHandleScope handles;
Handle<JSFunction> function = Compile("a + b");
CompilationInfoWithZone info(function);
- Linkage linkage(&info);
+ Linkage linkage(info.zone(), &info);
}
@@ -60,13 +59,13 @@
Handle<JSFunction> function = v8::Utils::OpenHandle(
*v8::Handle<v8::Function>::Cast(CompileRun(sources[i])));
CompilationInfoWithZone info(function);
- Linkage linkage(&info);
+ Linkage linkage(info.zone(), &info);
CallDescriptor* descriptor = linkage.GetIncomingDescriptor();
CHECK_NE(NULL, descriptor);
- CHECK_EQ(1 + i, descriptor->JSParameterCount());
- CHECK_EQ(1, descriptor->ReturnCount());
+ CHECK_EQ(1 + i, static_cast<int>(descriptor->JSParameterCount()));
+ CHECK_EQ(1, static_cast<int>(descriptor->ReturnCount()));
CHECK_EQ(Operator::kNoProperties, descriptor->properties());
CHECK_EQ(true, descriptor->IsJSFunctionCall());
}
@@ -76,7 +75,7 @@
TEST(TestLinkageCodeStubIncoming) {
Isolate* isolate = CcTest::InitIsolateOnce();
CompilationInfoWithZone info(static_cast<HydrogenCodeStub*>(NULL), isolate);
- Linkage linkage(&info);
+ Linkage linkage(info.zone(), &info);
// TODO(titzer): test linkage creation with a bonafide code stub.
// this just checks current behavior.
CHECK_EQ(NULL, linkage.GetIncomingDescriptor());
@@ -87,13 +86,14 @@
HandleAndZoneScope handles;
Handle<JSFunction> function = Compile("a + c");
CompilationInfoWithZone info(function);
- Linkage linkage(&info);
+ Linkage linkage(info.zone(), &info);
for (int i = 0; i < 32; i++) {
- CallDescriptor* descriptor = linkage.GetJSCallDescriptor(i);
+ CallDescriptor* descriptor =
+ linkage.GetJSCallDescriptor(i, CallDescriptor::kNoFlags);
CHECK_NE(NULL, descriptor);
- CHECK_EQ(i, descriptor->JSParameterCount());
- CHECK_EQ(1, descriptor->ReturnCount());
+ CHECK_EQ(i, static_cast<int>(descriptor->JSParameterCount()));
+ CHECK_EQ(1, static_cast<int>(descriptor->ReturnCount()));
CHECK_EQ(Operator::kNoProperties, descriptor->properties());
CHECK_EQ(true, descriptor->IsJSFunctionCall());
}
diff --git a/test/cctest/compiler/test-loop-analysis.cc b/test/cctest/compiler/test-loop-analysis.cc
new file mode 100644
index 0000000..9c11268
--- /dev/null
+++ b/test/cctest/compiler/test-loop-analysis.cc
@@ -0,0 +1,862 @@
+// 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 "src/compiler/access-builder.h"
+#include "src/compiler/common-operator.h"
+#include "src/compiler/graph.h"
+#include "src/compiler/graph-visualizer.h"
+#include "src/compiler/js-graph.h"
+#include "src/compiler/js-operator.h"
+#include "src/compiler/loop-analysis.h"
+#include "src/compiler/node.h"
+#include "src/compiler/opcodes.h"
+#include "src/compiler/operator.h"
+#include "src/compiler/schedule.h"
+#include "src/compiler/scheduler.h"
+#include "src/compiler/simplified-operator.h"
+#include "src/compiler/verifier.h"
+#include "test/cctest/cctest.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+static Operator kIntAdd(IrOpcode::kInt32Add, Operator::kPure, "Int32Add", 2, 0,
+ 0, 1, 0, 0);
+static Operator kIntLt(IrOpcode::kInt32LessThan, Operator::kPure,
+ "Int32LessThan", 2, 0, 0, 1, 0, 0);
+static Operator kStore(IrOpcode::kStore, Operator::kNoProperties, "Store", 0, 2,
+ 1, 0, 1, 0);
+
+static const int kNumLeafs = 4;
+
+// A helper for all tests dealing with LoopFinder.
+class LoopFinderTester : HandleAndZoneScope {
+ public:
+ LoopFinderTester()
+ : isolate(main_isolate()),
+ common(main_zone()),
+ graph(main_zone()),
+ jsgraph(&graph, &common, NULL, NULL),
+ start(graph.NewNode(common.Start(1))),
+ end(graph.NewNode(common.End(), start)),
+ p0(graph.NewNode(common.Parameter(0), start)),
+ zero(jsgraph.Int32Constant(0)),
+ one(jsgraph.OneConstant()),
+ half(jsgraph.Constant(0.5)),
+ self(graph.NewNode(common.Int32Constant(0xaabbccdd))),
+ dead(graph.NewNode(common.Dead())),
+ loop_tree(NULL) {
+ graph.SetEnd(end);
+ graph.SetStart(start);
+ leaf[0] = zero;
+ leaf[1] = one;
+ leaf[2] = half;
+ leaf[3] = p0;
+ }
+
+ Isolate* isolate;
+ CommonOperatorBuilder common;
+ Graph graph;
+ JSGraph jsgraph;
+ Node* start;
+ Node* end;
+ Node* p0;
+ Node* zero;
+ Node* one;
+ Node* half;
+ Node* self;
+ Node* dead;
+ Node* leaf[kNumLeafs];
+ LoopTree* loop_tree;
+
+ Node* Phi(Node* a) {
+ return SetSelfReferences(graph.NewNode(op(1, false), a, start));
+ }
+
+ Node* Phi(Node* a, Node* b) {
+ return SetSelfReferences(graph.NewNode(op(2, false), a, b, start));
+ }
+
+ Node* Phi(Node* a, Node* b, Node* c) {
+ return SetSelfReferences(graph.NewNode(op(3, false), a, b, c, start));
+ }
+
+ Node* Phi(Node* a, Node* b, Node* c, Node* d) {
+ return SetSelfReferences(graph.NewNode(op(4, false), a, b, c, d, start));
+ }
+
+ Node* EffectPhi(Node* a) {
+ return SetSelfReferences(graph.NewNode(op(1, true), a, start));
+ }
+
+ Node* EffectPhi(Node* a, Node* b) {
+ return SetSelfReferences(graph.NewNode(op(2, true), a, b, start));
+ }
+
+ Node* EffectPhi(Node* a, Node* b, Node* c) {
+ return SetSelfReferences(graph.NewNode(op(3, true), a, b, c, start));
+ }
+
+ Node* EffectPhi(Node* a, Node* b, Node* c, Node* d) {
+ return SetSelfReferences(graph.NewNode(op(4, true), a, b, c, d, start));
+ }
+
+ Node* SetSelfReferences(Node* node) {
+ for (Edge edge : node->input_edges()) {
+ if (edge.to() == self) node->ReplaceInput(edge.index(), node);
+ }
+ return node;
+ }
+
+ const Operator* op(int count, bool effect) {
+ return effect ? common.EffectPhi(count) : common.Phi(kMachAnyTagged, count);
+ }
+
+ Node* Return(Node* val, Node* effect, Node* control) {
+ Node* ret = graph.NewNode(common.Return(), val, effect, control);
+ end->ReplaceInput(0, ret);
+ return ret;
+ }
+
+ LoopTree* GetLoopTree() {
+ if (loop_tree == NULL) {
+ if (FLAG_trace_turbo_graph) {
+ OFStream os(stdout);
+ os << AsRPO(graph);
+ }
+ Zone zone(isolate);
+ loop_tree = LoopFinder::BuildLoopTree(&graph, &zone);
+ }
+ return loop_tree;
+ }
+
+ void CheckLoop(Node** header, int header_count, Node** body, int body_count) {
+ LoopTree* tree = GetLoopTree();
+ LoopTree::Loop* loop = tree->ContainingLoop(header[0]);
+ CHECK_NE(NULL, loop);
+
+ CHECK(header_count == static_cast<int>(loop->HeaderSize()));
+ for (int i = 0; i < header_count; i++) {
+ // Each header node should be in the loop.
+ CHECK_EQ(loop, tree->ContainingLoop(header[i]));
+ CheckRangeContains(tree->HeaderNodes(loop), header[i]);
+ }
+
+ CHECK_EQ(body_count, static_cast<int>(loop->BodySize()));
+ for (int i = 0; i < body_count; i++) {
+ // Each body node should be contained in the loop.
+ CHECK(tree->Contains(loop, body[i]));
+ CheckRangeContains(tree->BodyNodes(loop), body[i]);
+ }
+ }
+
+ void CheckRangeContains(NodeRange range, Node* node) {
+ // O(n) ftw.
+ CHECK_NE(range.end(), std::find(range.begin(), range.end(), node));
+ }
+
+ void CheckNestedLoops(Node** chain, int chain_count) {
+ LoopTree* tree = GetLoopTree();
+ for (int i = 0; i < chain_count; i++) {
+ Node* header = chain[i];
+ // Each header should be in a loop.
+ LoopTree::Loop* loop = tree->ContainingLoop(header);
+ CHECK_NE(NULL, loop);
+ // Check parentage.
+ LoopTree::Loop* parent =
+ i == 0 ? NULL : tree->ContainingLoop(chain[i - 1]);
+ CHECK_EQ(parent, loop->parent());
+ for (int j = i - 1; j >= 0; j--) {
+ // This loop should be nested inside all the outer loops.
+ Node* outer_header = chain[j];
+ LoopTree::Loop* outer = tree->ContainingLoop(outer_header);
+ CHECK(tree->Contains(outer, header));
+ CHECK(!tree->Contains(loop, outer_header));
+ }
+ }
+ }
+};
+
+
+struct While {
+ LoopFinderTester& t;
+ Node* branch;
+ Node* if_true;
+ Node* exit;
+ Node* loop;
+
+ While(LoopFinderTester& R, Node* cond) : t(R) {
+ loop = t.graph.NewNode(t.common.Loop(2), t.start, t.start);
+ 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(1, if_true);
+ }
+
+ void chain(Node* control) { loop->ReplaceInput(0, control); }
+ void nest(While& that) {
+ that.loop->ReplaceInput(1, exit);
+ this->loop->ReplaceInput(0, that.if_true);
+ }
+};
+
+
+struct Counter {
+ Node* base;
+ Node* inc;
+ Node* phi;
+ Node* add;
+
+ Counter(While& w, int32_t b, int32_t k)
+ : base(w.t.jsgraph.Int32Constant(b)), inc(w.t.jsgraph.Int32Constant(k)) {
+ Build(w);
+ }
+
+ Counter(While& w, Node* b, Node* k) : base(b), inc(k) { Build(w); }
+
+ void Build(While& w) {
+ phi = w.t.graph.NewNode(w.t.op(2, false), base, base, w.loop);
+ add = w.t.graph.NewNode(&kIntAdd, phi, inc);
+ phi->ReplaceInput(1, add);
+ }
+};
+
+
+struct StoreLoop {
+ Node* base;
+ Node* val;
+ Node* phi;
+ Node* store;
+
+ explicit StoreLoop(While& w)
+ : base(w.t.jsgraph.Int32Constant(12)),
+ val(w.t.jsgraph.Int32Constant(13)) {
+ Build(w);
+ }
+
+ StoreLoop(While& w, Node* b, Node* v) : base(b), val(v) { Build(w); }
+
+ void Build(While& w) {
+ phi = w.t.graph.NewNode(w.t.op(2, true), base, base, w.loop);
+ store = w.t.graph.NewNode(&kStore, phi, val, w.loop);
+ phi->ReplaceInput(1, store);
+ }
+};
+
+
+TEST(LaLoop1) {
+ // One loop.
+ LoopFinderTester t;
+ While w(t, t.p0);
+ t.Return(t.p0, t.start, w.exit);
+
+ Node* chain[] = {w.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w.loop};
+ Node* body[] = {w.branch, w.if_true};
+ t.CheckLoop(header, 1, body, 2);
+}
+
+
+TEST(LaLoop1c) {
+ // One loop with a counter.
+ LoopFinderTester t;
+ While w(t, t.p0);
+ Counter c(w, 0, 1);
+ t.Return(c.phi, t.start, w.exit);
+
+ Node* chain[] = {w.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w.loop, c.phi};
+ Node* body[] = {w.branch, w.if_true, c.add};
+ t.CheckLoop(header, 2, body, 3);
+}
+
+
+TEST(LaLoop1e) {
+ // One loop with an effect phi.
+ LoopFinderTester t;
+ While w(t, t.p0);
+ StoreLoop c(w);
+ t.Return(t.p0, c.phi, w.exit);
+
+ Node* chain[] = {w.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w.loop, c.phi};
+ Node* body[] = {w.branch, w.if_true, c.store};
+ t.CheckLoop(header, 2, body, 3);
+}
+
+
+TEST(LaLoop1d) {
+ // One loop with two counters.
+ LoopFinderTester t;
+ While w(t, t.p0);
+ Counter c1(w, 0, 1);
+ Counter c2(w, 1, 1);
+ t.Return(t.graph.NewNode(&kIntAdd, c1.phi, c2.phi), t.start, w.exit);
+
+ Node* chain[] = {w.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w.loop, c1.phi, c2.phi};
+ Node* body[] = {w.branch, w.if_true, c1.add, c2.add};
+ t.CheckLoop(header, 3, body, 4);
+}
+
+
+TEST(LaLoop2) {
+ // One loop following another.
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ While w2(t, t.p0);
+ w2.chain(w1.exit);
+ t.Return(t.p0, t.start, w2.exit);
+
+ {
+ Node* chain[] = {w1.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w1.loop};
+ Node* body[] = {w1.branch, w1.if_true};
+ t.CheckLoop(header, 1, body, 2);
+ }
+
+ {
+ Node* chain[] = {w2.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w2.loop};
+ Node* body[] = {w2.branch, w2.if_true};
+ t.CheckLoop(header, 1, body, 2);
+ }
+}
+
+
+TEST(LaLoop2c) {
+ // One loop following another, each with counters.
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ While w2(t, t.p0);
+ Counter c1(w1, 0, 1);
+ Counter c2(w2, 0, 1);
+ w2.chain(w1.exit);
+ t.Return(t.graph.NewNode(&kIntAdd, c1.phi, c2.phi), t.start, w2.exit);
+
+ {
+ Node* chain[] = {w1.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w1.loop, c1.phi};
+ Node* body[] = {w1.branch, w1.if_true, c1.add};
+ t.CheckLoop(header, 2, body, 3);
+ }
+
+ {
+ Node* chain[] = {w2.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w2.loop, c2.phi};
+ Node* body[] = {w2.branch, w2.if_true, c2.add};
+ t.CheckLoop(header, 2, body, 3);
+ }
+}
+
+
+TEST(LaLoop2cc) {
+ // One loop following another; second loop uses phi from first.
+ for (int i = 0; i < 8; i++) {
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ While w2(t, t.p0);
+ Counter c1(w1, 0, 1);
+
+ // various usage scenarios for the second loop.
+ Counter c2(w2, i & 1 ? t.p0 : c1.phi, i & 2 ? t.p0 : c1.phi);
+ if (i & 3) w2.branch->ReplaceInput(0, c1.phi);
+
+ w2.chain(w1.exit);
+ t.Return(t.graph.NewNode(&kIntAdd, c1.phi, c2.phi), t.start, w2.exit);
+
+ {
+ Node* chain[] = {w1.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w1.loop, c1.phi};
+ Node* body[] = {w1.branch, w1.if_true, c1.add};
+ t.CheckLoop(header, 2, body, 3);
+ }
+
+ {
+ Node* chain[] = {w2.loop};
+ t.CheckNestedLoops(chain, 1);
+
+ Node* header[] = {w2.loop, c2.phi};
+ Node* body[] = {w2.branch, w2.if_true, c2.add};
+ t.CheckLoop(header, 2, body, 3);
+ }
+ }
+}
+
+
+TEST(LaNestedLoop1) {
+ // One loop nested in another.
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ While w2(t, t.p0);
+ w2.nest(w1);
+ t.Return(t.p0, t.start, w1.exit);
+
+ Node* chain[] = {w1.loop, w2.loop};
+ t.CheckNestedLoops(chain, 2);
+
+ Node* h1[] = {w1.loop};
+ Node* b1[] = {w1.branch, w1.if_true, w2.loop, w2.branch, w2.if_true, w2.exit};
+ t.CheckLoop(h1, 1, b1, 6);
+
+ Node* h2[] = {w2.loop};
+ Node* b2[] = {w2.branch, w2.if_true};
+ t.CheckLoop(h2, 1, b2, 2);
+}
+
+
+TEST(LaNestedLoop1c) {
+ // One loop nested in another, each with a counter.
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ While w2(t, t.p0);
+ Counter c1(w1, 0, 1);
+ Counter c2(w2, 0, 1);
+ w2.branch->ReplaceInput(0, c2.phi);
+ w2.nest(w1);
+ t.Return(c1.phi, t.start, w1.exit);
+
+ Node* chain[] = {w1.loop, w2.loop};
+ t.CheckNestedLoops(chain, 2);
+
+ Node* h1[] = {w1.loop, c1.phi};
+ Node* b1[] = {w1.branch, w1.if_true, w2.loop, w2.branch, w2.if_true,
+ w2.exit, c2.phi, c1.add, c2.add};
+ t.CheckLoop(h1, 2, b1, 9);
+
+ Node* h2[] = {w2.loop, c2.phi};
+ Node* b2[] = {w2.branch, w2.if_true, c2.add};
+ t.CheckLoop(h2, 2, b2, 3);
+}
+
+
+TEST(LaNestedLoop2) {
+ // Two loops nested in an outer loop.
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ While w2(t, t.p0);
+ While w3(t, t.p0);
+ w2.nest(w1);
+ w3.nest(w1);
+ w3.chain(w2.exit);
+ t.Return(t.p0, t.start, w1.exit);
+
+ Node* chain1[] = {w1.loop, w2.loop};
+ t.CheckNestedLoops(chain1, 2);
+
+ Node* chain2[] = {w1.loop, w3.loop};
+ t.CheckNestedLoops(chain2, 2);
+
+ Node* h1[] = {w1.loop};
+ Node* b1[] = {w1.branch, w1.if_true, w2.loop, w2.branch, w2.if_true,
+ w2.exit, w3.loop, w3.branch, w3.if_true, w3.exit};
+ t.CheckLoop(h1, 1, b1, 10);
+
+ Node* h2[] = {w2.loop};
+ Node* b2[] = {w2.branch, w2.if_true};
+ t.CheckLoop(h2, 1, b2, 2);
+
+ Node* h3[] = {w3.loop};
+ Node* b3[] = {w3.branch, w3.if_true};
+ t.CheckLoop(h3, 1, b3, 2);
+}
+
+
+TEST(LaNestedLoop3) {
+ // Three nested loops.
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ While w2(t, t.p0);
+ While w3(t, t.p0);
+ w2.loop->ReplaceInput(0, w1.if_true);
+ w3.loop->ReplaceInput(0, w2.if_true);
+ w2.loop->ReplaceInput(1, w3.exit);
+ w1.loop->ReplaceInput(1, w2.exit);
+ t.Return(t.p0, t.start, w1.exit);
+
+ Node* chain[] = {w1.loop, w2.loop, w3.loop};
+ t.CheckNestedLoops(chain, 3);
+
+ Node* h1[] = {w1.loop};
+ Node* b1[] = {w1.branch, w1.if_true, w2.loop, w2.branch, w2.if_true,
+ w2.exit, w3.loop, w3.branch, w3.if_true, w3.exit};
+ t.CheckLoop(h1, 1, b1, 10);
+
+ Node* h2[] = {w2.loop};
+ Node* b2[] = {w2.branch, w2.if_true, w3.loop, w3.branch, w3.if_true, w3.exit};
+ t.CheckLoop(h2, 1, b2, 6);
+
+ Node* h3[] = {w3.loop};
+ Node* b3[] = {w3.branch, w3.if_true};
+ t.CheckLoop(h3, 1, b3, 2);
+}
+
+
+TEST(LaNestedLoop3c) {
+ // Three nested loops with counters.
+ LoopFinderTester t;
+ While w1(t, t.p0);
+ Counter c1(w1, 0, 1);
+ While w2(t, t.p0);
+ Counter c2(w2, 0, 1);
+ While w3(t, t.p0);
+ Counter c3(w3, 0, 1);
+ w2.loop->ReplaceInput(0, w1.if_true);
+ w3.loop->ReplaceInput(0, w2.if_true);
+ w2.loop->ReplaceInput(1, w3.exit);
+ w1.loop->ReplaceInput(1, w2.exit);
+ w1.branch->ReplaceInput(0, c1.phi);
+ w2.branch->ReplaceInput(0, c2.phi);
+ w3.branch->ReplaceInput(0, c3.phi);
+ t.Return(c1.phi, t.start, w1.exit);
+
+ Node* chain[] = {w1.loop, w2.loop, w3.loop};
+ t.CheckNestedLoops(chain, 3);
+
+ Node* h1[] = {w1.loop, c1.phi};
+ Node* b1[] = {w1.branch, w1.if_true, c1.add, c2.add, c2.add,
+ c2.phi, c3.phi, w2.loop, w2.branch, w2.if_true,
+ w2.exit, w3.loop, w3.branch, w3.if_true, w3.exit};
+ t.CheckLoop(h1, 2, b1, 15);
+
+ Node* h2[] = {w2.loop, c2.phi};
+ Node* b2[] = {w2.branch, w2.if_true, c2.add, c3.add, c3.phi,
+ w3.loop, w3.branch, w3.if_true, w3.exit};
+ t.CheckLoop(h2, 2, b2, 9);
+
+ Node* h3[] = {w3.loop, c3.phi};
+ Node* b3[] = {w3.branch, w3.if_true, c3.add};
+ t.CheckLoop(h3, 2, b3, 3);
+}
+
+
+TEST(LaMultipleExit1) {
+ const int kMaxExits = 10;
+ Node* merge[1 + kMaxExits];
+ Node* body[2 * kMaxExits];
+
+ // A single loop with {i} exits.
+ for (int i = 1; i < kMaxExits; i++) {
+ LoopFinderTester t;
+ Node* cond = t.p0;
+
+ int merge_count = 0;
+ int body_count = 0;
+ Node* loop = t.graph.NewNode(t.common.Loop(2), t.start, t.start);
+ Node* last = loop;
+
+ for (int e = 0; e < i; e++) {
+ Node* branch = t.graph.NewNode(t.common.Branch(), cond, last);
+ Node* if_true = t.graph.NewNode(t.common.IfTrue(), branch);
+ Node* exit = t.graph.NewNode(t.common.IfFalse(), branch);
+ last = if_true;
+
+ body[body_count++] = branch;
+ body[body_count++] = if_true;
+ merge[merge_count++] = exit;
+ }
+
+ loop->ReplaceInput(1, last); // form loop backedge.
+ Node* end = t.graph.NewNode(t.common.Merge(i), i, merge); // form exit.
+ t.graph.SetEnd(end);
+
+ Node* h[] = {loop};
+ t.CheckLoop(h, 1, body, body_count);
+ }
+}
+
+
+TEST(LaMultipleBackedge1) {
+ const int kMaxBackedges = 10;
+ Node* loop_inputs[1 + kMaxBackedges];
+ Node* body[3 * kMaxBackedges];
+
+ // A single loop with {i} backedges.
+ for (int i = 1; i < kMaxBackedges; i++) {
+ LoopFinderTester t;
+
+ for (int j = 0; j <= i; j++) loop_inputs[j] = t.start;
+ Node* loop = t.graph.NewNode(t.common.Loop(1 + i), 1 + i, loop_inputs);
+
+ Node* cond = t.p0;
+ int body_count = 0;
+ Node* exit = loop;
+
+ for (int b = 0; b < i; b++) {
+ Node* branch = t.graph.NewNode(t.common.Branch(), cond, exit);
+ Node* if_true = t.graph.NewNode(t.common.IfTrue(), branch);
+ Node* if_false = t.graph.NewNode(t.common.IfFalse(), branch);
+ exit = if_false;
+
+ body[body_count++] = branch;
+ body[body_count++] = if_true;
+ if (b != (i - 1)) body[body_count++] = if_false;
+
+ loop->ReplaceInput(1 + b, if_true);
+ }
+
+ t.graph.SetEnd(exit);
+
+ Node* h[] = {loop};
+ t.CheckLoop(h, 1, body, body_count);
+ }
+}
+
+
+TEST(LaEdgeMatrix1) {
+ // Test various kinds of extra edges added to a simple loop.
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ for (int k = 0; k < 3; k++) {
+ LoopFinderTester t;
+
+ Node* p1 = t.jsgraph.Int32Constant(11);
+ Node* p2 = t.jsgraph.Int32Constant(22);
+ Node* p3 = t.jsgraph.Int32Constant(33);
+
+ Node* loop = t.graph.NewNode(t.common.Loop(2), t.start, t.start);
+ Node* phi =
+ t.graph.NewNode(t.common.Phi(kMachInt32, 2), t.one, p1, loop);
+ Node* cond = t.graph.NewNode(&kIntAdd, phi, p2);
+ Node* branch = t.graph.NewNode(t.common.Branch(), cond, loop);
+ Node* if_true = t.graph.NewNode(t.common.IfTrue(), branch);
+ Node* exit = t.graph.NewNode(t.common.IfFalse(), branch);
+ loop->ReplaceInput(1, if_true);
+ Node* ret = t.graph.NewNode(t.common.Return(), p3, t.start, exit);
+ t.graph.SetEnd(ret);
+
+ Node* choices[] = {p1, phi, cond};
+ p1->ReplaceUses(choices[i]);
+ p2->ReplaceUses(choices[j]);
+ p3->ReplaceUses(choices[k]);
+
+ Node* header[] = {loop, phi};
+ Node* body[] = {cond, branch, if_true};
+ t.CheckLoop(header, 2, body, 3);
+ }
+ }
+ }
+}
+
+
+void RunEdgeMatrix2(int i) {
+ DCHECK(i >= 0 && i < 5);
+ for (int j = 0; j < 5; j++) {
+ for (int k = 0; k < 5; k++) {
+ LoopFinderTester t;
+
+ Node* p1 = t.jsgraph.Int32Constant(11);
+ Node* p2 = t.jsgraph.Int32Constant(22);
+ Node* p3 = t.jsgraph.Int32Constant(33);
+
+ // outer loop.
+ Node* loop1 = t.graph.NewNode(t.common.Loop(2), t.start, t.start);
+ Node* phi1 =
+ t.graph.NewNode(t.common.Phi(kMachInt32, 2), t.one, p1, loop1);
+ Node* cond1 = t.graph.NewNode(&kIntAdd, phi1, t.one);
+ Node* branch1 = t.graph.NewNode(t.common.Branch(), cond1, loop1);
+ Node* if_true1 = t.graph.NewNode(t.common.IfTrue(), branch1);
+ Node* exit1 = t.graph.NewNode(t.common.IfFalse(), branch1);
+
+ // inner loop.
+ Node* loop2 = t.graph.NewNode(t.common.Loop(2), if_true1, t.start);
+ Node* phi2 =
+ t.graph.NewNode(t.common.Phi(kMachInt32, 2), t.one, p2, loop2);
+ Node* cond2 = t.graph.NewNode(&kIntAdd, phi2, p3);
+ Node* branch2 = t.graph.NewNode(t.common.Branch(), cond2, loop2);
+ Node* if_true2 = t.graph.NewNode(t.common.IfTrue(), branch2);
+ Node* exit2 = t.graph.NewNode(t.common.IfFalse(), branch2);
+ loop2->ReplaceInput(1, if_true2);
+ loop1->ReplaceInput(1, exit2);
+
+ Node* ret = t.graph.NewNode(t.common.Return(), phi1, t.start, exit1);
+ t.graph.SetEnd(ret);
+
+ Node* choices[] = {p1, phi1, cond1, phi2, cond2};
+ p1->ReplaceUses(choices[i]);
+ p2->ReplaceUses(choices[j]);
+ p3->ReplaceUses(choices[k]);
+
+ Node* header1[] = {loop1, phi1};
+ Node* body1[] = {cond1, branch1, if_true1, exit2, loop2,
+ phi2, cond2, branch2, if_true2};
+ t.CheckLoop(header1, 2, body1, 9);
+
+ Node* header2[] = {loop2, phi2};
+ Node* body2[] = {cond2, branch2, if_true2};
+ t.CheckLoop(header2, 2, body2, 3);
+
+ Node* chain[] = {loop1, loop2};
+ t.CheckNestedLoops(chain, 2);
+ }
+ }
+}
+
+
+TEST(LaEdgeMatrix2_0) { RunEdgeMatrix2(0); }
+
+
+TEST(LaEdgeMatrix2_1) { RunEdgeMatrix2(1); }
+
+
+TEST(LaEdgeMatrix2_2) { RunEdgeMatrix2(2); }
+
+
+TEST(LaEdgeMatrix2_3) { RunEdgeMatrix2(3); }
+
+
+TEST(LaEdgeMatrix2_4) { RunEdgeMatrix2(4); }
+
+
+// Generates a triply-nested loop with extra edges between the phis and
+// conditions according to the edge choice parameters.
+void RunEdgeMatrix3(int c1a, int c1b, int c1c, // line break
+ int c2a, int c2b, int c2c, // line break
+ int c3a, int c3b, int c3c) { // line break
+ LoopFinderTester t;
+
+ Node* p1a = t.jsgraph.Int32Constant(11);
+ Node* p1b = t.jsgraph.Int32Constant(22);
+ Node* p1c = t.jsgraph.Int32Constant(33);
+ Node* p2a = t.jsgraph.Int32Constant(44);
+ Node* p2b = t.jsgraph.Int32Constant(55);
+ Node* p2c = t.jsgraph.Int32Constant(66);
+ Node* p3a = t.jsgraph.Int32Constant(77);
+ Node* p3b = t.jsgraph.Int32Constant(88);
+ Node* p3c = t.jsgraph.Int32Constant(99);
+
+ // L1 depth = 0
+ Node* loop1 = t.graph.NewNode(t.common.Loop(2), t.start, t.start);
+ Node* phi1 = t.graph.NewNode(t.common.Phi(kMachInt32, 2), p1a, p1c, loop1);
+ Node* cond1 = t.graph.NewNode(&kIntAdd, phi1, p1b);
+ Node* branch1 = t.graph.NewNode(t.common.Branch(), cond1, loop1);
+ Node* if_true1 = t.graph.NewNode(t.common.IfTrue(), branch1);
+ Node* exit1 = t.graph.NewNode(t.common.IfFalse(), branch1);
+
+ // L2 depth = 1
+ Node* loop2 = t.graph.NewNode(t.common.Loop(2), if_true1, t.start);
+ Node* phi2 = t.graph.NewNode(t.common.Phi(kMachInt32, 2), p2a, p2c, loop2);
+ Node* cond2 = t.graph.NewNode(&kIntAdd, phi2, p2b);
+ Node* branch2 = t.graph.NewNode(t.common.Branch(), cond2, loop2);
+ Node* if_true2 = t.graph.NewNode(t.common.IfTrue(), branch2);
+ Node* exit2 = t.graph.NewNode(t.common.IfFalse(), branch2);
+
+ // L3 depth = 2
+ Node* loop3 = t.graph.NewNode(t.common.Loop(2), if_true2, t.start);
+ Node* phi3 = t.graph.NewNode(t.common.Phi(kMachInt32, 2), p3a, p3c, loop3);
+ Node* cond3 = t.graph.NewNode(&kIntAdd, phi3, p3b);
+ Node* branch3 = t.graph.NewNode(t.common.Branch(), cond3, loop3);
+ Node* if_true3 = t.graph.NewNode(t.common.IfTrue(), branch3);
+ Node* exit3 = t.graph.NewNode(t.common.IfFalse(), branch3);
+
+ loop3->ReplaceInput(1, if_true3);
+ loop2->ReplaceInput(1, exit3);
+ loop1->ReplaceInput(1, exit2);
+
+ Node* ret = t.graph.NewNode(t.common.Return(), phi1, t.start, exit1);
+ t.graph.SetEnd(ret);
+
+ // Mutate the graph according to the edge choices.
+
+ Node* o1[] = {t.one};
+ Node* o2[] = {t.one, phi1, cond1};
+ Node* o3[] = {t.one, phi1, cond1, phi2, cond2};
+
+ p1a->ReplaceUses(o1[c1a]);
+ p1b->ReplaceUses(o1[c1b]);
+
+ p2a->ReplaceUses(o2[c2a]);
+ p2b->ReplaceUses(o2[c2b]);
+
+ p3a->ReplaceUses(o3[c3a]);
+ p3b->ReplaceUses(o3[c3b]);
+
+ Node* l2[] = {phi1, cond1, phi2, cond2};
+ Node* l3[] = {phi1, cond1, phi2, cond2, phi3, cond3};
+
+ p1c->ReplaceUses(l2[c1c]);
+ p2c->ReplaceUses(l3[c2c]);
+ p3c->ReplaceUses(l3[c3c]);
+
+ // Run the tests and verify loop structure.
+
+ Node* chain[] = {loop1, loop2, loop3};
+ t.CheckNestedLoops(chain, 3);
+
+ Node* header1[] = {loop1, phi1};
+ Node* body1[] = {cond1, branch1, if_true1, exit2, loop2,
+ phi2, cond2, branch2, if_true2, exit3,
+ loop3, phi3, cond3, branch3, if_true3};
+ t.CheckLoop(header1, 2, body1, 15);
+
+ Node* header2[] = {loop2, phi2};
+ Node* body2[] = {cond2, branch2, if_true2, exit3, loop3,
+ phi3, cond3, branch3, if_true3};
+ t.CheckLoop(header2, 2, body2, 9);
+
+ Node* header3[] = {loop3, phi3};
+ Node* body3[] = {cond3, branch3, if_true3};
+ t.CheckLoop(header3, 2, body3, 3);
+}
+
+
+// Runs all combinations with a fixed {i}.
+void RunEdgeMatrix3_i(int i) {
+ for (int a = 0; a < 1; a++) {
+ for (int b = 0; b < 1; b++) {
+ for (int c = 0; c < 4; c++) {
+ for (int d = 0; d < 3; d++) {
+ for (int e = 0; e < 3; e++) {
+ for (int f = 0; f < 6; f++) {
+ for (int g = 0; g < 5; g++) {
+ for (int h = 0; h < 5; h++) {
+ RunEdgeMatrix3(a, b, c, d, e, f, g, h, i);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+// Test all possible legal triply-nested loops with conditions and phis.
+TEST(LaEdgeMatrix3_0) { RunEdgeMatrix3_i(0); }
+
+
+TEST(LaEdgeMatrix3_1) { RunEdgeMatrix3_i(1); }
+
+
+TEST(LaEdgeMatrix3_2) { RunEdgeMatrix3_i(2); }
+
+
+TEST(LaEdgeMatrix3_3) { RunEdgeMatrix3_i(3); }
+
+
+TEST(LaEdgeMatrix3_4) { RunEdgeMatrix3_i(4); }
+
+
+TEST(LaEdgeMatrix3_5) { RunEdgeMatrix3_i(5); }
diff --git a/test/cctest/compiler/test-loop-assignment-analysis.cc b/test/cctest/compiler/test-loop-assignment-analysis.cc
new file mode 100644
index 0000000..aabd95b
--- /dev/null
+++ b/test/cctest/compiler/test-loop-assignment-analysis.cc
@@ -0,0 +1,294 @@
+// 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/ast-loop-assignment-analyzer.h"
+#include "src/parser.h"
+#include "src/rewriter.h"
+#include "src/scopes.h"
+#include "test/cctest/cctest.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+namespace {
+const int kBufferSize = 1024;
+
+struct TestHelper : public HandleAndZoneScope {
+ Handle<JSFunction> function;
+ LoopAssignmentAnalysis* result;
+
+ explicit TestHelper(const char* body)
+ : function(Handle<JSFunction>::null()), result(NULL) {
+ ScopedVector<char> program(kBufferSize);
+ SNPrintF(program, "function f(a,b,c) { %s; } f;", body);
+ v8::Local<v8::Value> v = CompileRun(program.start());
+ Handle<Object> obj = v8::Utils::OpenHandle(*v);
+ function = Handle<JSFunction>::cast(obj);
+ }
+
+ void CheckLoopAssignedCount(int expected, const char* var_name) {
+ // TODO(titzer): don't scope analyze every single time.
+ CompilationInfo info(function, main_zone());
+
+ CHECK(Parser::Parse(&info));
+ CHECK(Rewriter::Rewrite(&info));
+ CHECK(Scope::Analyze(&info));
+
+ Scope* scope = info.function()->scope();
+ AstValueFactory* factory = info.ast_value_factory();
+ CHECK_NE(NULL, scope);
+
+ if (result == NULL) {
+ AstLoopAssignmentAnalyzer analyzer(main_zone(), &info);
+ result = analyzer.Analyze();
+ CHECK_NE(NULL, result);
+ }
+
+ const i::AstRawString* name = factory->GetOneByteString(var_name);
+
+ i::Variable* var = scope->Lookup(name);
+ CHECK_NE(NULL, var);
+
+ if (var->location() == Variable::UNALLOCATED) {
+ CHECK_EQ(0, expected);
+ } else {
+ CHECK(var->IsStackAllocated());
+ CHECK_EQ(expected, result->GetAssignmentCountForTesting(scope, var));
+ }
+ }
+};
+}
+
+
+TEST(SimpleLoop1) {
+ TestHelper f("var x = 0; while (x) ;");
+
+ f.CheckLoopAssignedCount(0, "x");
+}
+
+
+TEST(SimpleLoop2) {
+ const char* loops[] = {
+ "while (x) { var x = 0; }", "for(;;) { var x = 0; }",
+ "for(;x;) { var x = 0; }", "for(;x;x) { var x = 0; }",
+ "for(var i = x; x; x) { var x = 0; }", "for(y in 0) { var x = 0; }",
+ "for(y of 0) { var x = 0; }", "for(var x = 0; x; x++) { }",
+ "for(var x = 0; x++;) { }", "var x; for(;x;x++) { }",
+ "var x; do { x = 1; } while (0);", "do { var x = 1; } while (0);"};
+
+ for (size_t i = 0; i < arraysize(loops); i++) {
+ TestHelper f(loops[i]);
+ f.CheckLoopAssignedCount(1, "x");
+ }
+}
+
+
+TEST(ForInOf1) {
+ const char* loops[] = {
+ "for(x in 0) { }", "for(x of 0) { }",
+ };
+
+ for (size_t i = 0; i < arraysize(loops); i++) {
+ TestHelper f(loops[i]);
+ f.CheckLoopAssignedCount(0, "x");
+ }
+}
+
+
+TEST(Param1) {
+ TestHelper f("while (1) a = 0;");
+
+ f.CheckLoopAssignedCount(1, "a");
+ f.CheckLoopAssignedCount(0, "b");
+ f.CheckLoopAssignedCount(0, "c");
+}
+
+
+TEST(Param2) {
+ TestHelper f("for (;;) b = 0;");
+
+ f.CheckLoopAssignedCount(0, "a");
+ f.CheckLoopAssignedCount(1, "b");
+ f.CheckLoopAssignedCount(0, "c");
+}
+
+
+TEST(Param2b) {
+ TestHelper f("a; b; c; for (;;) b = 0;");
+
+ f.CheckLoopAssignedCount(0, "a");
+ f.CheckLoopAssignedCount(1, "b");
+ f.CheckLoopAssignedCount(0, "c");
+}
+
+
+TEST(Param3) {
+ TestHelper f("for(x in 0) c = 0;");
+
+ f.CheckLoopAssignedCount(0, "a");
+ f.CheckLoopAssignedCount(0, "b");
+ f.CheckLoopAssignedCount(1, "c");
+}
+
+
+TEST(Param3b) {
+ TestHelper f("a; b; c; for(x in 0) c = 0;");
+
+ f.CheckLoopAssignedCount(0, "a");
+ f.CheckLoopAssignedCount(0, "b");
+ f.CheckLoopAssignedCount(1, "c");
+}
+
+
+TEST(NestedLoop1) {
+ TestHelper f("while (x) { while (x) { var x = 0; } }");
+
+ f.CheckLoopAssignedCount(2, "x");
+}
+
+
+TEST(NestedLoop2) {
+ TestHelper f("while (0) { while (0) { var x = 0; } }");
+
+ f.CheckLoopAssignedCount(2, "x");
+}
+
+
+TEST(NestedLoop3) {
+ TestHelper f("while (0) { var y = 1; while (0) { var x = 0; } }");
+
+ f.CheckLoopAssignedCount(2, "x");
+ f.CheckLoopAssignedCount(1, "y");
+}
+
+
+TEST(NestedInc1) {
+ const char* loops[] = {
+ "while (1) a(b++);",
+ "while (1) a(0, b++);",
+ "while (1) a(0, 0, b++);",
+ "while (1) a(b++, 1, 1);",
+ "while (1) a(++b);",
+ "while (1) a + (b++);",
+ "while (1) (b++) + a;",
+ "while (1) a + c(b++);",
+ "while (1) throw b++;",
+ "while (1) switch (b++) {} ;",
+ "while (1) switch (a) {case (b++): 0; } ;",
+ "while (1) switch (a) {case b: b++; } ;",
+ "while (1) a == (b++);",
+ "while (1) a === (b++);",
+ "while (1) +(b++);",
+ "while (1) ~(b++);",
+ "while (1) new a(b++);",
+ "while (1) (b++).f;",
+ "while (1) a[b++];",
+ "while (1) (b++)();",
+ "while (1) [b++];",
+ "while (1) [0,b++];",
+ "while (1) var y = [11,b++,12];",
+ "while (1) var y = {f:11,g:(b++),h:12};",
+ "while (1) try {b++;} finally {};",
+ "while (1) try {} finally {b++};",
+ "while (1) try {b++;} catch (e) {};",
+ "while (1) try {} catch (e) {b++};",
+ "while (1) return b++;",
+ "while (1) (b++) ? b : b;",
+ "while (1) b ? (b++) : b;",
+ "while (1) b ? b : (b++);",
+ };
+
+ for (size_t i = 0; i < arraysize(loops); i++) {
+ TestHelper f(loops[i]);
+ f.CheckLoopAssignedCount(1, "b");
+ }
+}
+
+
+TEST(NestedAssign1) {
+ const char* loops[] = {
+ "while (1) a(b=1);",
+ "while (1) a(0, b=1);",
+ "while (1) a(0, 0, b=1);",
+ "while (1) a(b=1, 1, 1);",
+ "while (1) a + (b=1);",
+ "while (1) (b=1) + a;",
+ "while (1) a + c(b=1);",
+ "while (1) throw b=1;",
+ "while (1) switch (b=1) {} ;",
+ "while (1) switch (a) {case b=1: 0; } ;",
+ "while (1) switch (a) {case b: b=1; } ;",
+ "while (1) a == (b=1);",
+ "while (1) a === (b=1);",
+ "while (1) +(b=1);",
+ "while (1) ~(b=1);",
+ "while (1) new a(b=1);",
+ "while (1) (b=1).f;",
+ "while (1) a[b=1];",
+ "while (1) (b=1)();",
+ "while (1) [b=1];",
+ "while (1) [0,b=1];",
+ "while (1) var z = [11,b=1,12];",
+ "while (1) var y = {f:11,g:(b=1),h:12};",
+ "while (1) try {b=1;} finally {};",
+ "while (1) try {} finally {b=1};",
+ "while (1) try {b=1;} catch (e) {};",
+ "while (1) try {} catch (e) {b=1};",
+ "while (1) return b=1;",
+ "while (1) (b=1) ? b : b;",
+ "while (1) b ? (b=1) : b;",
+ "while (1) b ? b : (b=1);",
+ };
+
+ for (size_t i = 0; i < arraysize(loops); i++) {
+ TestHelper f(loops[i]);
+ f.CheckLoopAssignedCount(1, "b");
+ }
+}
+
+
+TEST(NestedLoops3) {
+ TestHelper f("var x, y, z, w; while (x++) while (y++) while (z++) ; w;");
+
+ f.CheckLoopAssignedCount(1, "x");
+ f.CheckLoopAssignedCount(2, "y");
+ f.CheckLoopAssignedCount(3, "z");
+ f.CheckLoopAssignedCount(0, "w");
+}
+
+
+TEST(NestedLoops3b) {
+ TestHelper f(
+ "var x, y, z, w;"
+ "while (1) { x=1; while (1) { y=1; while (1) z=1; } }"
+ "w;");
+
+ f.CheckLoopAssignedCount(1, "x");
+ f.CheckLoopAssignedCount(2, "y");
+ f.CheckLoopAssignedCount(3, "z");
+ f.CheckLoopAssignedCount(0, "w");
+}
+
+
+TEST(NestedLoops3c) {
+ TestHelper f(
+ "var x, y, z, w;"
+ "while (1) {"
+ " x++;"
+ " while (1) {"
+ " y++;"
+ " while (1) z++;"
+ " }"
+ " while (1) {"
+ " y++;"
+ " while (1) z++;"
+ " }"
+ "}"
+ "w;");
+
+ f.CheckLoopAssignedCount(1, "x");
+ f.CheckLoopAssignedCount(3, "y");
+ f.CheckLoopAssignedCount(5, "z");
+ f.CheckLoopAssignedCount(0, "w");
+}
diff --git a/test/cctest/compiler/test-machine-operator-reducer.cc b/test/cctest/compiler/test-machine-operator-reducer.cc
index eca1f3c..648e1b9 100644
--- a/test/cctest/compiler/test-machine-operator-reducer.cc
+++ b/test/cctest/compiler/test-machine-operator-reducer.cc
@@ -5,9 +5,11 @@
#include "test/cctest/cctest.h"
#include "src/base/utils/random-number-generator.h"
+#include "src/codegen.h"
#include "src/compiler/graph-inl.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/machine-operator-reducer.h"
+#include "src/compiler/operator-properties.h"
#include "src/compiler/typer.h"
#include "test/cctest/compiler/value-helper.h"
@@ -49,15 +51,18 @@
class ReducerTester : public HandleAndZoneScope {
public:
- explicit ReducerTester(int num_parameters = 0)
+ explicit ReducerTester(
+ int num_parameters = 0,
+ MachineOperatorBuilder::Flags flags = MachineOperatorBuilder::kNoFlags)
: isolate(main_isolate()),
binop(NULL),
unop(NULL),
+ machine(main_zone(), kMachPtr, flags),
common(main_zone()),
graph(main_zone()),
javascript(main_zone()),
- typer(main_zone()),
- jsgraph(&graph, &common, &javascript, &typer, &machine),
+ typer(&graph, MaybeHandle<Context>()),
+ jsgraph(&graph, &common, &javascript, &machine),
maxuint32(Constant<int32_t>(kMaxUInt32)) {
Node* s = graph.NewNode(common.Start(num_parameters));
graph.SetStart(s);
@@ -96,7 +101,7 @@
template <typename T>
void CheckFoldBinop(volatile T expect, Node* a, Node* b) {
CHECK_NE(NULL, binop);
- Node* n = graph.NewNode(binop, a, b);
+ Node* n = CreateBinopNode(a, b);
MachineOperatorReducer reducer(&jsgraph);
Reduction reduction = reducer.Reduce(n);
CHECK(reduction.Changed());
@@ -108,7 +113,7 @@
// the {expect} node.
void CheckBinop(Node* expect, Node* a, Node* b) {
CHECK_NE(NULL, binop);
- Node* n = graph.NewNode(binop, a, b);
+ Node* n = CreateBinopNode(a, b);
MachineOperatorReducer reducer(&jsgraph);
Reduction reduction = reducer.Reduce(n);
CHECK(reduction.Changed());
@@ -120,7 +125,7 @@
void CheckFoldBinop(Node* left_expect, Node* right_expect, Node* left,
Node* right) {
CHECK_NE(NULL, binop);
- Node* n = graph.NewNode(binop, left, right);
+ Node* n = CreateBinopNode(left, right);
MachineOperatorReducer reducer(&jsgraph);
Reduction reduction = reducer.Reduce(n);
CHECK(reduction.Changed());
@@ -135,7 +140,7 @@
void CheckFoldBinop(volatile T left_expect, const Operator* op_expect,
Node* right_expect, Node* left, Node* right) {
CHECK_NE(NULL, binop);
- Node* n = graph.NewNode(binop, left, right);
+ Node* n = CreateBinopNode(left, right);
MachineOperatorReducer reducer(&jsgraph);
Reduction r = reducer.Reduce(n);
CHECK(r.Changed());
@@ -150,11 +155,13 @@
void CheckFoldBinop(Node* left_expect, const Operator* op_expect,
volatile T right_expect, Node* left, Node* right) {
CHECK_NE(NULL, binop);
- Node* n = graph.NewNode(binop, left, right);
+ Node* n = CreateBinopNode(left, right);
MachineOperatorReducer reducer(&jsgraph);
Reduction r = reducer.Reduce(n);
CHECK(r.Changed());
CHECK_EQ(op_expect->opcode(), r.replacement()->op()->opcode());
+ CHECK_EQ(OperatorProperties::GetTotalInputCount(op_expect),
+ r.replacement()->InputCount());
CHECK_EQ(left_expect, r.replacement()->InputAt(0));
CHECK_EQ(right_expect, ValueOf<T>(r.replacement()->InputAt(1)->op()));
}
@@ -167,7 +174,7 @@
Node* p = Parameter();
Node* k = Constant<T>(constant);
{
- Node* n = graph.NewNode(binop, k, p);
+ Node* n = CreateBinopNode(k, p);
MachineOperatorReducer reducer(&jsgraph);
Reduction reduction = reducer.Reduce(n);
CHECK(!reduction.Changed() || reduction.replacement() == n);
@@ -175,7 +182,7 @@
CHECK_EQ(k, n->InputAt(1));
}
{
- Node* n = graph.NewNode(binop, p, k);
+ Node* n = CreateBinopNode(p, k);
MachineOperatorReducer reducer(&jsgraph);
Reduction reduction = reducer.Reduce(n);
CHECK(!reduction.Changed());
@@ -191,7 +198,7 @@
CHECK(!binop->HasProperty(Operator::kCommutative));
Node* p = Parameter();
Node* k = Constant<T>(constant);
- Node* n = graph.NewNode(binop, k, p);
+ Node* n = CreateBinopNode(k, p);
MachineOperatorReducer reducer(&jsgraph);
Reduction reduction = reducer.Reduce(n);
CHECK(!reduction.Changed());
@@ -202,6 +209,15 @@
Node* Parameter(int32_t index = 0) {
return graph.NewNode(common.Parameter(index), graph.start());
}
+
+ private:
+ Node* CreateBinopNode(Node* left, Node* right) {
+ if (binop->ControlInputCount() > 0) {
+ return graph.NewNode(binop, left, right, graph.start());
+ } else {
+ return graph.NewNode(binop, left, right);
+ }
+ }
};
@@ -343,7 +359,36 @@
}
-TEST(ReduceWord32Equal) {
+static void CheckJsShift(ReducerTester* R) {
+ DCHECK(R->machine.Word32ShiftIsSafe());
+
+ Node* x = R->Parameter(0);
+ Node* y = R->Parameter(1);
+ Node* thirty_one = R->Constant<int32_t>(0x1f);
+ Node* y_and_thirty_one =
+ R->graph.NewNode(R->machine.Word32And(), y, thirty_one);
+
+ // If the underlying machine shift instructions 'and' their right operand
+ // with 0x1f then: x << (y & 0x1f) => x << y
+ R->CheckFoldBinop(x, y, x, y_and_thirty_one);
+}
+
+
+TEST(ReduceJsShifts) {
+ ReducerTester R(0, MachineOperatorBuilder::kWord32ShiftIsSafe);
+
+ R.binop = R.machine.Word32Shl();
+ CheckJsShift(&R);
+
+ R.binop = R.machine.Word32Shr();
+ CheckJsShift(&R);
+
+ R.binop = R.machine.Word32Sar();
+ CheckJsShift(&R);
+}
+
+
+TEST(Word32Equal) {
ReducerTester R;
R.binop = R.machine.Word32Equal();
@@ -476,9 +521,9 @@
}
-TEST(ReduceInt32UDiv) {
+TEST(ReduceUint32Div) {
ReducerTester R;
- R.binop = R.machine.Int32UDiv();
+ R.binop = R.machine.Uint32Div();
FOR_UINT32_INPUTS(pl) {
FOR_UINT32_INPUTS(pr) {
@@ -529,9 +574,9 @@
}
-TEST(ReduceInt32UMod) {
+TEST(ReduceUint32Mod) {
ReducerTester R;
- R.binop = R.machine.Int32UMod();
+ R.binop = R.machine.Uint32Mod();
FOR_INT32_INPUTS(pl) {
FOR_INT32_INPUTS(pr) {
@@ -687,9 +732,9 @@
pr != nans.end(); ++pr) {
Node* nan1 = R->Constant<double>(*pl);
Node* nan2 = R->Constant<double>(*pr);
- R->CheckBinop(nan1, x, nan1); // x % NaN => NaN
- R->CheckBinop(nan1, nan1, x); // NaN % x => NaN
- R->CheckBinop(nan1, nan2, nan1); // NaN % NaN => NaN
+ R->CheckBinop(nan1, x, nan1); // x op NaN => NaN
+ R->CheckBinop(nan1, nan1, x); // NaN op x => NaN
+ R->CheckBinop(nan1, nan2, nan1); // NaN op NaN => NaN
}
}
}
@@ -706,8 +751,15 @@
}
}
- FOR_FLOAT64_INPUTS(i) { R.CheckPutConstantOnRight(*i); }
- // TODO(titzer): CheckNans(&R);
+ FOR_FLOAT64_INPUTS(i) {
+ Double tmp(*i);
+ if (!tmp.IsSpecial() || tmp.IsInfinite()) {
+ // Don't check NaNs as they are reduced more.
+ R.CheckPutConstantOnRight(*i);
+ }
+ }
+
+ CheckNans(&R);
}
@@ -721,7 +773,13 @@
R.CheckFoldBinop<double>(x - y, x, y);
}
}
- // TODO(titzer): CheckNans(&R);
+
+ Node* zero = R.Constant<double>(0.0);
+ Node* x = R.Parameter();
+
+ R.CheckBinop(x, x, zero); // x - 0.0 => x
+
+ CheckNans(&R);
}
@@ -783,6 +841,11 @@
}
}
+ Node* x = R.Parameter();
+ Node* zero = R.Constant<double>(0.0);
+
+ R.CheckFoldBinop<double>(v8::base::OS::nan_value(), x, zero);
+
CheckNans(&R);
}
@@ -800,9 +863,9 @@
// TODO(titzer): test MachineOperatorReducer for Int64Mul
// TODO(titzer): test MachineOperatorReducer for Int64UMul
// TODO(titzer): test MachineOperatorReducer for Int64Div
-// TODO(titzer): test MachineOperatorReducer for Int64UDiv
+// TODO(titzer): test MachineOperatorReducer for Uint64Div
// TODO(titzer): test MachineOperatorReducer for Int64Mod
-// TODO(titzer): test MachineOperatorReducer for Int64UMod
+// TODO(titzer): test MachineOperatorReducer for Uint64Mod
// TODO(titzer): test MachineOperatorReducer for Int64Neg
// TODO(titzer): test MachineOperatorReducer for ChangeInt32ToFloat64
// TODO(titzer): test MachineOperatorReducer for ChangeFloat64ToInt32
diff --git a/test/cctest/compiler/test-node-algorithm.cc b/test/cctest/compiler/test-node-algorithm.cc
index 10f98a6..842d182 100644
--- a/test/cctest/compiler/test-node-algorithm.cc
+++ b/test/cctest/compiler/test-node-algorithm.cc
@@ -8,25 +8,23 @@
#include "graph-tester.h"
#include "src/compiler/common-operator.h"
-#include "src/compiler/generic-node.h"
-#include "src/compiler/generic-node-inl.h"
#include "src/compiler/graph.h"
#include "src/compiler/graph-inl.h"
#include "src/compiler/graph-visualizer.h"
+#include "src/compiler/node.h"
#include "src/compiler/operator.h"
using namespace v8::internal;
using namespace v8::internal::compiler;
-static SimpleOperator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
- 0, 0, "dummy");
+static Operator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
+ "dummy", 0, 0, 0, 1, 0, 0);
class PreNodeVisitor : public NullNodeVisitor {
public:
- GenericGraphVisit::Control Pre(Node* node) {
+ void Pre(Node* node) {
printf("NODE ID: %d\n", node->id());
nodes_.push_back(node);
- return GenericGraphVisit::CONTINUE;
}
std::vector<Node*> nodes_;
};
@@ -34,45 +32,14 @@
class PostNodeVisitor : public NullNodeVisitor {
public:
- GenericGraphVisit::Control Post(Node* node) {
+ void Post(Node* node) {
printf("NODE ID: %d\n", node->id());
nodes_.push_back(node);
- return GenericGraphVisit::CONTINUE;
}
std::vector<Node*> nodes_;
};
-TEST(TestUseNodeVisitEmpty) {
- GraphWithStartNodeTester graph;
-
- PreNodeVisitor node_visitor;
- graph.VisitNodeUsesFromStart(&node_visitor);
-
- CHECK_EQ(1, static_cast<int>(node_visitor.nodes_.size()));
-}
-
-
-TEST(TestUseNodePreOrderVisitSimple) {
- GraphWithStartNodeTester graph;
- Node* n2 = graph.NewNode(&dummy_operator, graph.start());
- Node* n3 = graph.NewNode(&dummy_operator, n2);
- Node* n4 = graph.NewNode(&dummy_operator, n2, n3);
- Node* n5 = graph.NewNode(&dummy_operator, n4, n2);
- graph.SetEnd(n5);
-
- PreNodeVisitor node_visitor;
- graph.VisitNodeUsesFromStart(&node_visitor);
-
- CHECK_EQ(5, static_cast<int>(node_visitor.nodes_.size()));
- CHECK(graph.start()->id() == node_visitor.nodes_[0]->id());
- CHECK(n2->id() == node_visitor.nodes_[1]->id());
- CHECK(n3->id() == node_visitor.nodes_[2]->id());
- CHECK(n4->id() == node_visitor.nodes_[3]->id());
- CHECK(n5->id() == node_visitor.nodes_[4]->id());
-}
-
-
TEST(TestInputNodePreOrderVisitSimple) {
GraphWithStartNodeTester graph;
Node* n2 = graph.NewNode(&dummy_operator, graph.start());
@@ -92,223 +59,6 @@
}
-TEST(TestUseNodePostOrderVisitSimple) {
- GraphWithStartNodeTester graph;
- Node* n2 = graph.NewNode(&dummy_operator, graph.start());
- Node* n3 = graph.NewNode(&dummy_operator, graph.start());
- Node* n4 = graph.NewNode(&dummy_operator, n2);
- Node* n5 = graph.NewNode(&dummy_operator, n2);
- Node* n6 = graph.NewNode(&dummy_operator, n2);
- Node* n7 = graph.NewNode(&dummy_operator, n3);
- Node* end_dependencies[4] = {n4, n5, n6, n7};
- Node* n8 = graph.NewNode(&dummy_operator, 4, end_dependencies);
- graph.SetEnd(n8);
-
- PostNodeVisitor node_visitor;
- graph.VisitNodeUsesFromStart(&node_visitor);
-
- CHECK_EQ(8, static_cast<int>(node_visitor.nodes_.size()));
- CHECK(graph.end()->id() == node_visitor.nodes_[0]->id());
- CHECK(n4->id() == node_visitor.nodes_[1]->id());
- CHECK(n5->id() == node_visitor.nodes_[2]->id());
- CHECK(n6->id() == node_visitor.nodes_[3]->id());
- CHECK(n2->id() == node_visitor.nodes_[4]->id());
- CHECK(n7->id() == node_visitor.nodes_[5]->id());
- CHECK(n3->id() == node_visitor.nodes_[6]->id());
- CHECK(graph.start()->id() == node_visitor.nodes_[7]->id());
-}
-
-
-TEST(TestUseNodePostOrderVisitLong) {
- GraphWithStartNodeTester graph;
- Node* n2 = graph.NewNode(&dummy_operator, graph.start());
- Node* n3 = graph.NewNode(&dummy_operator, graph.start());
- Node* n4 = graph.NewNode(&dummy_operator, n2);
- Node* n5 = graph.NewNode(&dummy_operator, n2);
- Node* n6 = graph.NewNode(&dummy_operator, n3);
- Node* n7 = graph.NewNode(&dummy_operator, n3);
- Node* n8 = graph.NewNode(&dummy_operator, n5);
- Node* n9 = graph.NewNode(&dummy_operator, n5);
- Node* n10 = graph.NewNode(&dummy_operator, n9);
- Node* n11 = graph.NewNode(&dummy_operator, n9);
- Node* end_dependencies[6] = {n4, n8, n10, n11, n6, n7};
- Node* n12 = graph.NewNode(&dummy_operator, 6, end_dependencies);
- graph.SetEnd(n12);
-
- PostNodeVisitor node_visitor;
- graph.VisitNodeUsesFromStart(&node_visitor);
-
- CHECK_EQ(12, static_cast<int>(node_visitor.nodes_.size()));
- CHECK(graph.end()->id() == node_visitor.nodes_[0]->id());
- CHECK(n4->id() == node_visitor.nodes_[1]->id());
- CHECK(n8->id() == node_visitor.nodes_[2]->id());
- CHECK(n10->id() == node_visitor.nodes_[3]->id());
- CHECK(n11->id() == node_visitor.nodes_[4]->id());
- CHECK(n9->id() == node_visitor.nodes_[5]->id());
- CHECK(n5->id() == node_visitor.nodes_[6]->id());
- CHECK(n2->id() == node_visitor.nodes_[7]->id());
- CHECK(n6->id() == node_visitor.nodes_[8]->id());
- CHECK(n7->id() == node_visitor.nodes_[9]->id());
- CHECK(n3->id() == node_visitor.nodes_[10]->id());
- CHECK(graph.start()->id() == node_visitor.nodes_[11]->id());
-}
-
-
-TEST(TestUseNodePreOrderVisitCycle) {
- GraphWithStartNodeTester graph;
- Node* n0 = graph.start_node();
- Node* n1 = graph.NewNode(&dummy_operator, n0);
- Node* n2 = graph.NewNode(&dummy_operator, n1);
- n0->AppendInput(graph.main_zone(), n2);
- graph.SetStart(n0);
- graph.SetEnd(n2);
-
- PreNodeVisitor node_visitor;
- graph.VisitNodeUsesFromStart(&node_visitor);
-
- CHECK_EQ(3, static_cast<int>(node_visitor.nodes_.size()));
- CHECK(n0->id() == node_visitor.nodes_[0]->id());
- CHECK(n1->id() == node_visitor.nodes_[1]->id());
- CHECK(n2->id() == node_visitor.nodes_[2]->id());
-}
-
-
-struct ReenterNodeVisitor : NullNodeVisitor {
- GenericGraphVisit::Control Pre(Node* node) {
- printf("[%d] PRE NODE: %d\n", static_cast<int>(nodes_.size()), node->id());
- nodes_.push_back(node->id());
- int size = static_cast<int>(nodes_.size());
- switch (node->id()) {
- case 0:
- return size < 6 ? GenericGraphVisit::REENTER : GenericGraphVisit::SKIP;
- case 1:
- return size < 4 ? GenericGraphVisit::DEFER
- : GenericGraphVisit::CONTINUE;
- default:
- return GenericGraphVisit::REENTER;
- }
- }
-
- GenericGraphVisit::Control Post(Node* node) {
- printf("[%d] POST NODE: %d\n", static_cast<int>(nodes_.size()), node->id());
- nodes_.push_back(-node->id());
- return node->id() == 4 ? GenericGraphVisit::REENTER
- : GenericGraphVisit::CONTINUE;
- }
-
- void PreEdge(Node* from, int index, Node* to) {
- printf("[%d] PRE EDGE: %d-%d\n", static_cast<int>(edges_.size()),
- from->id(), to->id());
- edges_.push_back(std::make_pair(from->id(), to->id()));
- }
-
- void PostEdge(Node* from, int index, Node* to) {
- printf("[%d] POST EDGE: %d-%d\n", static_cast<int>(edges_.size()),
- from->id(), to->id());
- edges_.push_back(std::make_pair(-from->id(), -to->id()));
- }
-
- std::vector<int> nodes_;
- std::vector<std::pair<int, int> > edges_;
-};
-
-
-TEST(TestUseNodeReenterVisit) {
- GraphWithStartNodeTester graph;
- Node* n0 = graph.start_node();
- Node* n1 = graph.NewNode(&dummy_operator, n0);
- Node* n2 = graph.NewNode(&dummy_operator, n0);
- Node* n3 = graph.NewNode(&dummy_operator, n2);
- Node* n4 = graph.NewNode(&dummy_operator, n0);
- Node* n5 = graph.NewNode(&dummy_operator, n4);
- n0->AppendInput(graph.main_zone(), n3);
- graph.SetStart(n0);
- graph.SetEnd(n5);
-
- ReenterNodeVisitor visitor;
- graph.VisitNodeUsesFromStart(&visitor);
-
- CHECK_EQ(22, static_cast<int>(visitor.nodes_.size()));
- CHECK_EQ(24, static_cast<int>(visitor.edges_.size()));
-
- CHECK(n0->id() == visitor.nodes_[0]);
- CHECK(n0->id() == visitor.edges_[0].first);
- CHECK(n1->id() == visitor.edges_[0].second);
- CHECK(n1->id() == visitor.nodes_[1]);
- // N1 is deferred.
- CHECK(-n1->id() == visitor.edges_[1].second);
- CHECK(-n0->id() == visitor.edges_[1].first);
- CHECK(n0->id() == visitor.edges_[2].first);
- CHECK(n2->id() == visitor.edges_[2].second);
- CHECK(n2->id() == visitor.nodes_[2]);
- CHECK(n2->id() == visitor.edges_[3].first);
- CHECK(n3->id() == visitor.edges_[3].second);
- CHECK(n3->id() == visitor.nodes_[3]);
- // Circle back to N0, which we may reenter for now.
- CHECK(n3->id() == visitor.edges_[4].first);
- CHECK(n0->id() == visitor.edges_[4].second);
- CHECK(n0->id() == visitor.nodes_[4]);
- CHECK(n0->id() == visitor.edges_[5].first);
- CHECK(n1->id() == visitor.edges_[5].second);
- CHECK(n1->id() == visitor.nodes_[5]);
- // This time N1 is no longer deferred.
- CHECK(-n1->id() == visitor.nodes_[6]);
- CHECK(-n1->id() == visitor.edges_[6].second);
- CHECK(-n0->id() == visitor.edges_[6].first);
- CHECK(n0->id() == visitor.edges_[7].first);
- CHECK(n2->id() == visitor.edges_[7].second);
- CHECK(n2->id() == visitor.nodes_[7]);
- CHECK(n2->id() == visitor.edges_[8].first);
- CHECK(n3->id() == visitor.edges_[8].second);
- CHECK(n3->id() == visitor.nodes_[8]);
- CHECK(n3->id() == visitor.edges_[9].first);
- CHECK(n0->id() == visitor.edges_[9].second);
- CHECK(n0->id() == visitor.nodes_[9]);
- // This time we break at N0 and skip it.
- CHECK(-n0->id() == visitor.edges_[10].second);
- CHECK(-n3->id() == visitor.edges_[10].first);
- CHECK(-n3->id() == visitor.nodes_[10]);
- CHECK(-n3->id() == visitor.edges_[11].second);
- CHECK(-n2->id() == visitor.edges_[11].first);
- CHECK(-n2->id() == visitor.nodes_[11]);
- CHECK(-n2->id() == visitor.edges_[12].second);
- CHECK(-n0->id() == visitor.edges_[12].first);
- CHECK(n0->id() == visitor.edges_[13].first);
- CHECK(n4->id() == visitor.edges_[13].second);
- CHECK(n4->id() == visitor.nodes_[12]);
- CHECK(n4->id() == visitor.edges_[14].first);
- CHECK(n5->id() == visitor.edges_[14].second);
- CHECK(n5->id() == visitor.nodes_[13]);
- CHECK(-n5->id() == visitor.nodes_[14]);
- CHECK(-n5->id() == visitor.edges_[15].second);
- CHECK(-n4->id() == visitor.edges_[15].first);
- CHECK(-n4->id() == visitor.nodes_[15]);
- CHECK(-n4->id() == visitor.edges_[16].second);
- CHECK(-n0->id() == visitor.edges_[16].first);
- CHECK(-n0->id() == visitor.nodes_[16]);
- CHECK(-n0->id() == visitor.edges_[17].second);
- CHECK(-n3->id() == visitor.edges_[17].first);
- CHECK(-n3->id() == visitor.nodes_[17]);
- CHECK(-n3->id() == visitor.edges_[18].second);
- CHECK(-n2->id() == visitor.edges_[18].first);
- CHECK(-n2->id() == visitor.nodes_[18]);
- CHECK(-n2->id() == visitor.edges_[19].second);
- CHECK(-n0->id() == visitor.edges_[19].first);
- // N4 may be reentered.
- CHECK(n0->id() == visitor.edges_[20].first);
- CHECK(n4->id() == visitor.edges_[20].second);
- CHECK(n4->id() == visitor.nodes_[19]);
- CHECK(n4->id() == visitor.edges_[21].first);
- CHECK(n5->id() == visitor.edges_[21].second);
- CHECK(-n5->id() == visitor.edges_[22].second);
- CHECK(-n4->id() == visitor.edges_[22].first);
- CHECK(-n4->id() == visitor.nodes_[20]);
- CHECK(-n4->id() == visitor.edges_[23].second);
- CHECK(-n0->id() == visitor.edges_[23].first);
- CHECK(-n0->id() == visitor.nodes_[21]);
-}
-
-
TEST(TestPrintNodeGraphToNodeGraphviz) {
GraphWithStartNodeTester graph;
Node* n2 = graph.NewNode(&dummy_operator, graph.start());
diff --git a/test/cctest/compiler/test-node-cache.cc b/test/cctest/compiler/test-node-cache.cc
index 3569386..a48adb9 100644
--- a/test/cctest/compiler/test-node-cache.cc
+++ b/test/cctest/compiler/test-node-cache.cc
@@ -115,46 +115,61 @@
}
-TEST(PtrConstant_back_to_back) {
- GraphTester graph;
- PtrNodeCache cache;
- int32_t buffer[50];
+static bool Contains(NodeVector* nodes, Node* n) {
+ for (size_t i = 0; i < nodes->size(); i++) {
+ if (nodes->at(i) == n) return true;
+ }
+ return false;
+}
- for (int32_t* p = buffer;
- (p - buffer) < static_cast<ptrdiff_t>(arraysize(buffer)); p++) {
- Node** pos = cache.Find(graph.zone(), p);
- CHECK_NE(NULL, pos);
- for (int j = 0; j < 3; j++) {
- Node** npos = cache.Find(graph.zone(), p);
- CHECK_EQ(pos, npos);
+
+TEST(NodeCache_GetCachedNodes_int32) {
+ GraphTester graph;
+ Int32NodeCache cache;
+ CommonOperatorBuilder common(graph.zone());
+
+ int32_t constants[] = {0, 311, 12, 13, 14, 555, -555, -44, -33, -22, -11,
+ 0, 311, 311, 412, 412, 11, 11, -33, -33, -22, -11};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int32_t k = constants[i];
+ Node** pos = cache.Find(graph.zone(), k);
+ if (*pos != NULL) {
+ NodeVector nodes(graph.zone());
+ cache.GetCachedNodes(&nodes);
+ CHECK(Contains(&nodes, *pos));
+ } else {
+ NodeVector nodes(graph.zone());
+ Node* n = graph.NewNode(common.Int32Constant(k));
+ *pos = n;
+ cache.GetCachedNodes(&nodes);
+ CHECK(Contains(&nodes, n));
}
}
}
-TEST(PtrConstant_hits) {
+TEST(NodeCache_GetCachedNodes_int64) {
GraphTester graph;
- PtrNodeCache cache;
- const int32_t kSize = 50;
- int32_t buffer[kSize];
- Node* nodes[kSize];
+ Int64NodeCache cache;
CommonOperatorBuilder common(graph.zone());
- for (size_t i = 0; i < arraysize(buffer); i++) {
- int k = static_cast<int>(i);
- int32_t* p = &buffer[i];
- nodes[i] = graph.NewNode(common.Int32Constant(k));
- *cache.Find(graph.zone(), p) = nodes[i];
- }
+ int64_t constants[] = {0, 311, 12, 13, 14, 555, -555, -44, -33, -22, -11,
+ 0, 311, 311, 412, 412, 11, 11, -33, -33, -22, -11};
- int hits = 0;
- for (size_t i = 0; i < arraysize(buffer); i++) {
- int32_t* p = &buffer[i];
- Node** pos = cache.Find(graph.zone(), p);
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int64_t k = constants[i];
+ Node** pos = cache.Find(graph.zone(), k);
if (*pos != NULL) {
- CHECK_EQ(nodes[i], *pos);
- hits++;
+ NodeVector nodes(graph.zone());
+ cache.GetCachedNodes(&nodes);
+ CHECK(Contains(&nodes, *pos));
+ } else {
+ NodeVector nodes(graph.zone());
+ Node* n = graph.NewNode(common.Int64Constant(k));
+ *pos = n;
+ cache.GetCachedNodes(&nodes);
+ CHECK(Contains(&nodes, n));
}
}
- CHECK_LT(4, hits);
}
diff --git a/test/cctest/compiler/test-node.cc b/test/cctest/compiler/test-node.cc
index 28d807e..eafabd3 100644
--- a/test/cctest/compiler/test-node.cc
+++ b/test/cctest/compiler/test-node.cc
@@ -7,15 +7,14 @@
#include "src/v8.h"
#include "graph-tester.h"
-#include "src/compiler/generic-node-inl.h"
#include "src/compiler/node.h"
#include "src/compiler/operator.h"
using namespace v8::internal;
using namespace v8::internal::compiler;
-static SimpleOperator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
- 0, 0, "dummy");
+static Operator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
+ "dummy", 0, 0, 0, 1, 0, 0);
TEST(NodeAllocation) {
GraphTester graph;
@@ -93,10 +92,8 @@
TEST(NodeUseIteratorEmpty) {
GraphTester graph;
Node* n1 = graph.NewNode(&dummy_operator);
- Node::Uses::iterator i(n1->uses().begin());
int use_count = 0;
- for (; i != n1->uses().end(); ++i) {
- Node::Edge edge(i.edge());
+ for (Edge const edge : n1->use_edges()) {
USE(edge);
use_count++;
}
@@ -366,31 +363,31 @@
Node* n1 = graph.NewNode(&dummy_operator, n0);
Node* n2 = graph.NewNode(&dummy_operator, n0, n1);
- Node::Inputs inputs(n2->inputs());
- Node::Inputs::iterator current = inputs.begin();
+ Node::InputEdges inputs(n2->input_edges());
+ Node::InputEdges::iterator current = inputs.begin();
CHECK(current != inputs.end());
- CHECK(*current == n0);
+ CHECK((*current).to() == n0);
++current;
CHECK(current != inputs.end());
- CHECK(*current == n1);
+ CHECK((*current).to() == n1);
++current;
CHECK(current == inputs.end());
Node* n3 = graph.NewNode(&dummy_operator);
n2->AppendInput(graph.zone(), n3);
- inputs = n2->inputs();
+ inputs = n2->input_edges();
current = inputs.begin();
CHECK(current != inputs.end());
- CHECK(*current == n0);
- CHECK_EQ(0, current.index());
+ CHECK((*current).to() == n0);
+ CHECK_EQ(0, (*current).index());
++current;
CHECK(current != inputs.end());
- CHECK(*current == n1);
- CHECK_EQ(1, current.index());
+ CHECK((*current).to() == n1);
+ CHECK_EQ(1, (*current).index());
++current;
CHECK(current != inputs.end());
- CHECK(*current == n3);
- CHECK_EQ(2, current.index());
+ CHECK((*current).to() == n3);
+ CHECK_EQ(2, (*current).index());
++current;
CHECK(current == inputs.end());
}
diff --git a/test/cctest/compiler/test-operator.cc b/test/cctest/compiler/test-operator.cc
index af75d67..39f660f 100644
--- a/test/cctest/compiler/test-operator.cc
+++ b/test/cctest/compiler/test-operator.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <sstream>
+
#include "src/v8.h"
#include "src/compiler/operator.h"
@@ -10,44 +12,45 @@
using namespace v8::internal;
using namespace v8::internal::compiler;
-#define NaN (v8::base::OS::nan_value())
-#define Infinity (std::numeric_limits<double>::infinity())
+#define NONE Operator::kNoProperties
+#define FOLD Operator::kFoldable
-TEST(TestOperatorMnemonic) {
- SimpleOperator op1(10, Operator::kNoProperties, 0, 0, "ThisOne");
+
+TEST(TestOperator_Mnemonic) {
+ Operator op1(10, NONE, "ThisOne", 0, 0, 0, 0, 0, 0);
CHECK_EQ(0, strcmp(op1.mnemonic(), "ThisOne"));
- SimpleOperator op2(11, Operator::kNoProperties, 0, 0, "ThatOne");
+ Operator op2(11, NONE, "ThatOne", 0, 0, 0, 0, 0, 0);
CHECK_EQ(0, strcmp(op2.mnemonic(), "ThatOne"));
- Operator1<int> op3(12, Operator::kNoProperties, 0, 1, "Mnemonic1", 12333);
+ Operator1<int> op3(12, NONE, "Mnemonic1", 0, 0, 0, 1, 0, 0, 12333);
CHECK_EQ(0, strcmp(op3.mnemonic(), "Mnemonic1"));
- Operator1<double> op4(13, Operator::kNoProperties, 0, 1, "TheOther", 99.9);
+ Operator1<double> op4(13, NONE, "TheOther", 0, 0, 0, 1, 0, 0, 99.9);
CHECK_EQ(0, strcmp(op4.mnemonic(), "TheOther"));
}
-TEST(TestSimpleOperatorHash) {
- SimpleOperator op1(17, Operator::kNoProperties, 0, 0, "Another");
- CHECK_EQ(17, op1.HashCode());
+TEST(TestOperator_Hash) {
+ Operator op1(17, NONE, "Another", 0, 0, 0, 0, 0, 0);
+ CHECK_EQ(17, static_cast<int>(op1.HashCode()));
- SimpleOperator op2(18, Operator::kNoProperties, 0, 0, "Falsch");
- CHECK_EQ(18, op2.HashCode());
+ Operator op2(18, NONE, "Falsch", 0, 0, 0, 0, 0, 0);
+ CHECK_EQ(18, static_cast<int>(op2.HashCode()));
}
-TEST(TestSimpleOperatorEquals) {
- SimpleOperator op1a(19, Operator::kNoProperties, 0, 0, "Another1");
- SimpleOperator op1b(19, Operator::kFoldable, 2, 2, "Another2");
+TEST(TestOperator_Equals) {
+ Operator op1a(19, NONE, "Another1", 0, 0, 0, 0, 0, 0);
+ Operator op1b(19, FOLD, "Another2", 2, 0, 0, 2, 0, 0);
CHECK(op1a.Equals(&op1a));
CHECK(op1a.Equals(&op1b));
CHECK(op1b.Equals(&op1a));
CHECK(op1b.Equals(&op1b));
- SimpleOperator op2a(20, Operator::kNoProperties, 0, 0, "Falsch1");
- SimpleOperator op2b(20, Operator::kFoldable, 1, 1, "Falsch2");
+ Operator op2a(20, NONE, "Falsch1", 0, 0, 0, 0, 0, 0);
+ Operator op2b(20, FOLD, "Falsch2", 1, 0, 0, 1, 0, 0);
CHECK(op2a.Equals(&op2a));
CHECK(op2a.Equals(&op2b));
@@ -67,52 +70,52 @@
static SmartArrayPointer<const char> OperatorToString(Operator* op) {
- OStringStream os;
+ std::ostringstream os;
os << *op;
- return SmartArrayPointer<const char>(StrDup(os.c_str()));
+ return SmartArrayPointer<const char>(StrDup(os.str().c_str()));
}
-TEST(TestSimpleOperatorPrint) {
- SimpleOperator op1a(19, Operator::kNoProperties, 0, 0, "Another1");
- SimpleOperator op1b(19, Operator::kFoldable, 2, 2, "Another2");
+TEST(TestOperator_Print) {
+ Operator op1a(19, NONE, "Another1", 0, 0, 0, 0, 0, 0);
+ Operator op1b(19, FOLD, "Another2", 2, 0, 0, 2, 0, 0);
CHECK_EQ("Another1", OperatorToString(&op1a).get());
CHECK_EQ("Another2", OperatorToString(&op1b).get());
- SimpleOperator op2a(20, Operator::kNoProperties, 0, 0, "Flog1");
- SimpleOperator op2b(20, Operator::kFoldable, 1, 1, "Flog2");
+ Operator op2a(20, NONE, "Flog1", 0, 0, 0, 0, 0, 0);
+ Operator op2b(20, FOLD, "Flog2", 1, 0, 0, 1, 0, 0);
CHECK_EQ("Flog1", OperatorToString(&op2a).get());
CHECK_EQ("Flog2", OperatorToString(&op2b).get());
}
-TEST(TestOperator1intHash) {
- Operator1<int> op1a(23, Operator::kNoProperties, 0, 0, "Wolfie", 11);
- Operator1<int> op1b(23, Operator::kFoldable, 2, 2, "Doggie", 11);
+TEST(TestOperator1int_Hash) {
+ Operator1<int> op1a(23, NONE, "Wolfie", 0, 0, 0, 0, 0, 0, 11);
+ Operator1<int> op1b(23, FOLD, "Doggie", 2, 0, 0, 2, 0, 0, 11);
- CHECK_EQ(op1a.HashCode(), op1b.HashCode());
+ CHECK(op1a.HashCode() == op1b.HashCode());
- Operator1<int> op2a(24, Operator::kNoProperties, 0, 0, "Arfie", 3);
- Operator1<int> op2b(24, Operator::kNoProperties, 0, 0, "Arfie", 4);
+ Operator1<int> op2a(24, NONE, "Arfie", 0, 0, 0, 0, 0, 0, 3);
+ Operator1<int> op2b(24, NONE, "Arfie", 0, 0, 0, 0, 0, 0, 4);
- CHECK_NE(op1a.HashCode(), op2a.HashCode());
- CHECK_NE(op2a.HashCode(), op2b.HashCode());
+ CHECK(op1a.HashCode() != op2a.HashCode());
+ CHECK(op2a.HashCode() != op2b.HashCode());
}
-TEST(TestOperator1intEquals) {
- Operator1<int> op1a(23, Operator::kNoProperties, 0, 0, "Scratchy", 11);
- Operator1<int> op1b(23, Operator::kFoldable, 2, 2, "Scratchy", 11);
+TEST(TestOperator1int_Equals) {
+ Operator1<int> op1a(23, NONE, "Scratchy", 0, 0, 0, 0, 0, 0, 11);
+ Operator1<int> op1b(23, FOLD, "Scratchy", 2, 0, 0, 2, 0, 0, 11);
CHECK(op1a.Equals(&op1a));
CHECK(op1a.Equals(&op1b));
CHECK(op1b.Equals(&op1a));
CHECK(op1b.Equals(&op1b));
- Operator1<int> op2a(24, Operator::kNoProperties, 0, 0, "Im", 3);
- Operator1<int> op2b(24, Operator::kNoProperties, 0, 0, "Im", 4);
+ Operator1<int> op2a(24, NONE, "Im", 0, 0, 0, 0, 0, 0, 3);
+ Operator1<int> op2b(24, NONE, "Im", 0, 0, 0, 0, 0, 0, 4);
CHECK(op2a.Equals(&op2a));
CHECK(!op2a.Equals(&op2b));
@@ -129,7 +132,7 @@
CHECK(!op2b.Equals(&op1a));
CHECK(!op2b.Equals(&op1b));
- SimpleOperator op3(25, Operator::kNoProperties, 0, 0, "Weepy");
+ Operator op3(25, NONE, "Weepy", 0, 0, 0, 0, 0, 0);
CHECK(!op1a.Equals(&op3));
CHECK(!op1b.Equals(&op3));
@@ -143,46 +146,55 @@
}
-TEST(TestOperator1intPrint) {
- Operator1<int> op1(12, Operator::kNoProperties, 0, 1, "Op1Test", 0);
+TEST(TestOperator1int_Print) {
+ Operator1<int> op1(12, NONE, "Op1Test", 0, 0, 0, 1, 0, 0, 0);
CHECK_EQ("Op1Test[0]", OperatorToString(&op1).get());
- Operator1<int> op2(12, Operator::kNoProperties, 0, 1, "Op1Test", 66666666);
+ Operator1<int> op2(12, NONE, "Op1Test", 0, 0, 0, 1, 0, 0, 66666666);
CHECK_EQ("Op1Test[66666666]", OperatorToString(&op2).get());
- Operator1<int> op3(12, Operator::kNoProperties, 0, 1, "FooBar", 2347);
+ Operator1<int> op3(12, NONE, "FooBar", 0, 0, 0, 1, 0, 0, 2347);
CHECK_EQ("FooBar[2347]", OperatorToString(&op3).get());
- Operator1<int> op4(12, Operator::kNoProperties, 0, 1, "BarFoo", -879);
+ Operator1<int> op4(12, NONE, "BarFoo", 0, 0, 0, 1, 0, 0, -879);
CHECK_EQ("BarFoo[-879]", OperatorToString(&op4).get());
}
-TEST(TestOperator1doubleHash) {
- Operator1<double> op1a(23, Operator::kNoProperties, 0, 0, "Wolfie", 11.77);
- Operator1<double> op1b(23, Operator::kFoldable, 2, 2, "Doggie", 11.77);
+TEST(TestOperator1double_Hash) {
+ Operator1<double> op1a(23, NONE, "Wolfie", 0, 0, 0, 0, 0, 0, 11.77);
+ Operator1<double> op1b(23, FOLD, "Doggie", 2, 0, 0, 2, 0, 0, 11.77);
- CHECK_EQ(op1a.HashCode(), op1b.HashCode());
+ CHECK(op1a.HashCode() == op1b.HashCode());
- Operator1<double> op2a(24, Operator::kNoProperties, 0, 0, "Arfie", -6.7);
- Operator1<double> op2b(24, Operator::kNoProperties, 0, 0, "Arfie", -6.8);
+ Operator1<double> op2a(24, NONE, "Arfie", 0, 0, 0, 0, 0, 0, -6.7);
+ Operator1<double> op2b(24, NONE, "Arfie", 0, 0, 0, 0, 0, 0, -6.8);
- CHECK_NE(op1a.HashCode(), op2a.HashCode());
- CHECK_NE(op2a.HashCode(), op2b.HashCode());
+ CHECK(op1a.HashCode() != op2a.HashCode());
+ CHECK(op2a.HashCode() != op2b.HashCode());
}
-TEST(TestOperator1doubleEquals) {
- Operator1<double> op1a(23, Operator::kNoProperties, 0, 0, "Scratchy", 11.77);
- Operator1<double> op1b(23, Operator::kFoldable, 2, 2, "Scratchy", 11.77);
+TEST(TestOperator1doublePrint) {
+ Operator1<double> op1a(23, NONE, "Canary", 0, 0, 0, 0, 0, 0, 0.5);
+ Operator1<double> op1b(23, FOLD, "Finch", 2, 0, 0, 2, 0, 0, -1.5);
+
+ CHECK_EQ("Canary[0.5]", OperatorToString(&op1a).get());
+ CHECK_EQ("Finch[-1.5]", OperatorToString(&op1b).get());
+}
+
+
+TEST(TestOperator1double_Equals) {
+ Operator1<double> op1a(23, NONE, "Scratchy", 0, 0, 0, 0, 0, 0, 11.77);
+ Operator1<double> op1b(23, FOLD, "Scratchy", 2, 0, 0, 2, 0, 0, 11.77);
CHECK(op1a.Equals(&op1a));
CHECK(op1a.Equals(&op1b));
CHECK(op1b.Equals(&op1a));
CHECK(op1b.Equals(&op1b));
- Operator1<double> op2a(24, Operator::kNoProperties, 0, 0, "Im", 3.1);
- Operator1<double> op2b(24, Operator::kNoProperties, 0, 0, "Im", 3.2);
+ Operator1<double> op2a(24, NONE, "Im", 0, 0, 0, 0, 0, 0, 3.1);
+ Operator1<double> op2b(24, NONE, "Im", 0, 0, 0, 0, 0, 0, 3.2);
CHECK(op2a.Equals(&op2a));
CHECK(!op2a.Equals(&op2b));
@@ -199,7 +211,7 @@
CHECK(!op2b.Equals(&op1a));
CHECK(!op2b.Equals(&op1b));
- SimpleOperator op3(25, Operator::kNoProperties, 0, 0, "Weepy");
+ Operator op3(25, NONE, "Weepy", 0, 0, 0, 0, 0, 0);
CHECK(!op1a.Equals(&op3));
CHECK(!op1b.Equals(&op3));
@@ -211,8 +223,8 @@
CHECK(!op3.Equals(&op2a));
CHECK(!op3.Equals(&op2b));
- Operator1<double> op4a(24, Operator::kNoProperties, 0, 0, "Bashful", NaN);
- Operator1<double> op4b(24, Operator::kNoProperties, 0, 0, "Bashful", NaN);
+ Operator1<double> op4a(24, NONE, "Bashful", 0, 0, 0, 0, 0, 0, 1.0);
+ Operator1<double> op4b(24, NONE, "Bashful", 0, 0, 0, 0, 0, 0, 1.0);
CHECK(op4a.Equals(&op4a));
CHECK(op4a.Equals(&op4b));
@@ -226,19 +238,46 @@
}
-TEST(TestOperator1doublePrint) {
- Operator1<double> op1(12, Operator::kNoProperties, 0, 1, "Op1Test", 0);
- CHECK_EQ("Op1Test[0]", OperatorToString(&op1).get());
+TEST(TestOpParameter_Operator1double) {
+ double values[] = {7777.5, -66, 0, 11, 0.1};
- Operator1<double> op2(12, Operator::kNoProperties, 0, 1, "Op1Test", 7.3);
- CHECK_EQ("Op1Test[7.3]", OperatorToString(&op2).get());
+ for (size_t i = 0; i < arraysize(values); i++) {
+ Operator1<double> op(33, NONE, "Scurvy", 0, 0, 0, 0, 0, 0, values[i]);
+ CHECK_EQ(values[i], OpParameter<double>(&op));
+ }
+}
- Operator1<double> op3(12, Operator::kNoProperties, 0, 1, "FooBar", 2e+123);
- CHECK_EQ("FooBar[2e+123]", OperatorToString(&op3).get());
- Operator1<double> op4(12, Operator::kNoProperties, 0, 1, "BarFoo", Infinity);
- CHECK_EQ("BarFoo[inf]", OperatorToString(&op4).get());
+TEST(TestOpParameter_Operator1float) {
+ float values[] = {// thanks C++.
+ static_cast<float>(7777.5), static_cast<float>(-66),
+ static_cast<float>(0), static_cast<float>(11),
+ static_cast<float>(0.1)};
- Operator1<double> op5(12, Operator::kNoProperties, 0, 1, "BarFoo", NaN);
- CHECK_EQ("BarFoo[nan]", OperatorToString(&op5).get());
+ for (size_t i = 0; i < arraysize(values); i++) {
+ Operator1<float> op(33, NONE, "Scurvy", 0, 0, 0, 0, 0, 0, values[i]);
+ CHECK_EQ(values[i], OpParameter<float>(&op));
+ }
+}
+
+
+TEST(TestOpParameter_Operator1int) {
+ int values[] = {7777, -66, 0, 11, 1, 0x666aff};
+
+ for (size_t i = 0; i < arraysize(values); i++) {
+ Operator1<int> op(33, NONE, "Scurvy", 0, 0, 0, 0, 0, 0, values[i]);
+ CHECK_EQ(values[i], OpParameter<int>(&op));
+ }
+}
+
+
+TEST(Operator_CountsOrder) {
+ Operator op(29, NONE, "Flashy", 11, 22, 33, 44, 55, 66);
+ CHECK_EQ(11, op.ValueInputCount());
+ CHECK_EQ(22, op.EffectInputCount());
+ CHECK_EQ(33, op.ControlInputCount());
+
+ CHECK_EQ(44, op.ValueOutputCount());
+ CHECK_EQ(55, op.EffectOutputCount());
+ CHECK_EQ(66, op.ControlOutputCount());
}
diff --git a/test/cctest/compiler/test-phi-reducer.cc b/test/cctest/compiler/test-phi-reducer.cc
deleted file mode 100644
index 7d2fab6..0000000
--- a/test/cctest/compiler/test-phi-reducer.cc
+++ /dev/null
@@ -1,230 +0,0 @@
-// 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/graph-inl.h"
-#include "src/compiler/phi-reducer.h"
-
-using namespace v8::internal;
-using namespace v8::internal::compiler;
-
-class PhiReducerTester : HandleAndZoneScope {
- public:
- explicit PhiReducerTester(int num_parameters = 0)
- : isolate(main_isolate()),
- common(main_zone()),
- graph(main_zone()),
- self(graph.NewNode(common.Start(num_parameters))),
- dead(graph.NewNode(common.Dead())) {
- graph.SetStart(self);
- }
-
- Isolate* isolate;
- CommonOperatorBuilder common;
- Graph graph;
- Node* self;
- Node* dead;
-
- void CheckReduce(Node* expect, Node* phi) {
- PhiReducer reducer;
- Reduction reduction = reducer.Reduce(phi);
- if (expect == phi) {
- CHECK(!reduction.Changed());
- } else {
- CHECK(reduction.Changed());
- CHECK_EQ(expect, reduction.replacement());
- }
- }
-
- Node* Int32Constant(int32_t val) {
- return graph.NewNode(common.Int32Constant(val));
- }
-
- Node* Float64Constant(double val) {
- return graph.NewNode(common.Float64Constant(val));
- }
-
- Node* Parameter(int32_t index = 0) {
- return graph.NewNode(common.Parameter(index), graph.start());
- }
-
- Node* Phi(Node* a) {
- return SetSelfReferences(graph.NewNode(common.Phi(kMachAnyTagged, 1), a));
- }
-
- Node* Phi(Node* a, Node* b) {
- return SetSelfReferences(
- graph.NewNode(common.Phi(kMachAnyTagged, 2), a, b));
- }
-
- Node* Phi(Node* a, Node* b, Node* c) {
- return SetSelfReferences(
- graph.NewNode(common.Phi(kMachAnyTagged, 3), a, b, c));
- }
-
- Node* Phi(Node* a, Node* b, Node* c, Node* d) {
- return SetSelfReferences(
- graph.NewNode(common.Phi(kMachAnyTagged, 4), a, b, c, d));
- }
-
- Node* PhiWithControl(Node* a, Node* control) {
- return SetSelfReferences(
- graph.NewNode(common.Phi(kMachAnyTagged, 1), a, control));
- }
-
- Node* PhiWithControl(Node* a, Node* b, Node* control) {
- return SetSelfReferences(
- graph.NewNode(common.Phi(kMachAnyTagged, 2), a, b, control));
- }
-
- Node* SetSelfReferences(Node* node) {
- Node::Inputs inputs = node->inputs();
- for (Node::Inputs::iterator iter(inputs.begin()); iter != inputs.end();
- ++iter) {
- Node* input = *iter;
- if (input == self) node->ReplaceInput(iter.index(), node);
- }
- return node;
- }
-};
-
-
-TEST(PhiReduce1) {
- PhiReducerTester R;
- Node* zero = R.Int32Constant(0);
- Node* one = R.Int32Constant(1);
- Node* oneish = R.Float64Constant(1.1);
- Node* param = R.Parameter();
-
- Node* singles[] = {zero, one, oneish, param};
- for (size_t i = 0; i < arraysize(singles); i++) {
- R.CheckReduce(singles[i], R.Phi(singles[i]));
- }
-}
-
-
-TEST(PhiReduce2) {
- PhiReducerTester R;
- Node* zero = R.Int32Constant(0);
- Node* one = R.Int32Constant(1);
- Node* oneish = R.Float64Constant(1.1);
- Node* param = R.Parameter();
-
- Node* singles[] = {zero, one, oneish, param};
- for (size_t i = 0; i < arraysize(singles); i++) {
- Node* a = singles[i];
- R.CheckReduce(a, R.Phi(a, a));
- }
-
- for (size_t i = 0; i < arraysize(singles); i++) {
- Node* a = singles[i];
- R.CheckReduce(a, R.Phi(R.self, a));
- R.CheckReduce(a, R.Phi(a, R.self));
- }
-
- for (size_t i = 1; i < arraysize(singles); i++) {
- Node* a = singles[i], *b = singles[0];
- Node* phi1 = R.Phi(b, a);
- R.CheckReduce(phi1, phi1);
-
- Node* phi2 = R.Phi(a, b);
- R.CheckReduce(phi2, phi2);
- }
-}
-
-
-TEST(PhiReduce3) {
- PhiReducerTester R;
- Node* zero = R.Int32Constant(0);
- Node* one = R.Int32Constant(1);
- Node* oneish = R.Float64Constant(1.1);
- Node* param = R.Parameter();
-
- Node* singles[] = {zero, one, oneish, param};
- for (size_t i = 0; i < arraysize(singles); i++) {
- Node* a = singles[i];
- R.CheckReduce(a, R.Phi(a, a, a));
- }
-
- for (size_t i = 0; i < arraysize(singles); i++) {
- Node* a = singles[i];
- R.CheckReduce(a, R.Phi(R.self, a, a));
- R.CheckReduce(a, R.Phi(a, R.self, a));
- R.CheckReduce(a, R.Phi(a, a, R.self));
- }
-
- for (size_t i = 1; i < arraysize(singles); i++) {
- Node* a = singles[i], *b = singles[0];
- Node* phi1 = R.Phi(b, a, a);
- R.CheckReduce(phi1, phi1);
-
- Node* phi2 = R.Phi(a, b, a);
- R.CheckReduce(phi2, phi2);
-
- Node* phi3 = R.Phi(a, a, b);
- R.CheckReduce(phi3, phi3);
- }
-}
-
-
-TEST(PhiReduce4) {
- PhiReducerTester R;
- Node* zero = R.Int32Constant(0);
- Node* one = R.Int32Constant(1);
- Node* oneish = R.Float64Constant(1.1);
- Node* param = R.Parameter();
-
- Node* singles[] = {zero, one, oneish, param};
- for (size_t i = 0; i < arraysize(singles); i++) {
- Node* a = singles[i];
- R.CheckReduce(a, R.Phi(a, a, a, a));
- }
-
- for (size_t i = 0; i < arraysize(singles); i++) {
- Node* a = singles[i];
- R.CheckReduce(a, R.Phi(R.self, a, a, a));
- R.CheckReduce(a, R.Phi(a, R.self, a, a));
- R.CheckReduce(a, R.Phi(a, a, R.self, a));
- R.CheckReduce(a, R.Phi(a, a, a, R.self));
-
- R.CheckReduce(a, R.Phi(R.self, R.self, a, a));
- R.CheckReduce(a, R.Phi(a, R.self, R.self, a));
- R.CheckReduce(a, R.Phi(a, a, R.self, R.self));
- R.CheckReduce(a, R.Phi(R.self, a, a, R.self));
- }
-
- for (size_t i = 1; i < arraysize(singles); i++) {
- Node* a = singles[i], *b = singles[0];
- Node* phi1 = R.Phi(b, a, a, a);
- R.CheckReduce(phi1, phi1);
-
- Node* phi2 = R.Phi(a, b, a, a);
- R.CheckReduce(phi2, phi2);
-
- Node* phi3 = R.Phi(a, a, b, a);
- R.CheckReduce(phi3, phi3);
-
- Node* phi4 = R.Phi(a, a, a, b);
- R.CheckReduce(phi4, phi4);
- }
-}
-
-
-TEST(PhiReduceShouldIgnoreControlNodes) {
- PhiReducerTester R;
- Node* zero = R.Int32Constant(0);
- Node* one = R.Int32Constant(1);
- Node* oneish = R.Float64Constant(1.1);
- Node* param = R.Parameter();
-
- Node* singles[] = {zero, one, oneish, param};
- for (size_t i = 0; i < arraysize(singles); ++i) {
- R.CheckReduce(singles[i], R.PhiWithControl(singles[i], R.dead));
- R.CheckReduce(singles[i], R.PhiWithControl(R.self, singles[i], R.dead));
- R.CheckReduce(singles[i], R.PhiWithControl(singles[i], R.self, R.dead));
- }
-}
diff --git a/test/cctest/compiler/test-pipeline.cc b/test/cctest/compiler/test-pipeline.cc
index f0b750a..98b0bae 100644
--- a/test/cctest/compiler/test-pipeline.cc
+++ b/test/cctest/compiler/test-pipeline.cc
@@ -5,6 +5,7 @@
#include "src/v8.h"
#include "test/cctest/cctest.h"
+#include "src/ast-numbering.h"
#include "src/compiler.h"
#include "src/compiler/pipeline.h"
#include "src/handles.h"
@@ -22,10 +23,7 @@
*v8::Handle<v8::Function>::Cast(CompileRun(source)));
CompilationInfoWithZone info(function);
- CHECK(Parser::Parse(&info));
- CHECK(Rewriter::Rewrite(&info));
- CHECK(Scope::Analyze(&info));
- CHECK_NE(NULL, info.scope());
+ CHECK(Compiler::ParseAndAnalyze(&info));
Pipeline pipeline(&info);
#if V8_TURBOFAN_TARGET
diff --git a/test/cctest/compiler/test-representation-change.cc b/test/cctest/compiler/test-representation-change.cc
index 6c9026b..2dc3029 100644
--- a/test/cctest/compiler/test-representation-change.cc
+++ b/test/cctest/compiler/test-representation-change.cc
@@ -7,10 +7,10 @@
#include "src/v8.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/graph-builder-tester.h"
+#include "test/cctest/compiler/value-helper.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/representation-change.h"
-#include "src/compiler/typer.h"
using namespace v8::internal;
using namespace v8::internal::compiler;
@@ -24,16 +24,13 @@
public:
explicit RepresentationChangerTester(int num_parameters = 0)
: GraphAndBuilders(main_zone()),
- typer_(main_zone()),
javascript_(main_zone()),
- jsgraph_(main_graph_, &main_common_, &javascript_, &typer_,
- &main_machine_),
+ jsgraph_(main_graph_, &main_common_, &javascript_, &main_machine_),
changer_(&jsgraph_, &main_simplified_, main_isolate()) {
Node* s = graph()->NewNode(common()->Start(num_parameters));
graph()->SetStart(s);
}
- Typer typer_;
JSOperatorBuilder javascript_;
JSGraph jsgraph_;
RepresentationChanger changer_;
@@ -51,6 +48,24 @@
CHECK_EQ(expected, m.Value());
}
+ void CheckUint32Constant(Node* n, uint32_t expected) {
+ Uint32Matcher m(n);
+ CHECK(m.HasValue());
+ CHECK_EQ(static_cast<int>(expected), static_cast<int>(m.Value()));
+ }
+
+ void CheckFloat64Constant(Node* n, double expected) {
+ Float64Matcher m(n);
+ CHECK(m.HasValue());
+ CHECK_EQ(expected, m.Value());
+ }
+
+ void CheckFloat32Constant(Node* n, float expected) {
+ CHECK_EQ(IrOpcode::kFloat32Constant, n->opcode());
+ float fval = OpParameter<float>(n->op());
+ CHECK_EQ(expected, fval);
+ }
+
void CheckHeapConstant(Node* n, HeapObject* expected) {
HeapObjectMatcher<HeapObject> m(n);
CHECK(m.HasValue());
@@ -88,28 +103,8 @@
} // namespace v8::internal::compiler
-// TODO(titzer): add kRepFloat32 when fully supported.
-static const MachineType all_reps[] = {kRepBit, kRepWord32, kRepWord64,
- kRepFloat64, kRepTagged};
-
-
-// TODO(titzer): lift this to ValueHelper
-static const double double_inputs[] = {
- 0.0, -0.0, 1.0, -1.0, 0.1, 1.4, -1.7,
- 2, 5, 6, 982983, 888, -999.8, 3.1e7,
- -2e66, 2.3e124, -12e73, V8_INFINITY, -V8_INFINITY};
-
-
-static const int32_t int32_inputs[] = {
- 0, 1, -1,
- 2, 5, 6,
- 982983, 888, -999,
- 65535, static_cast<int32_t>(0xFFFFFFFF), static_cast<int32_t>(0x80000000)};
-
-
-static const uint32_t uint32_inputs[] = {
- 0, 1, static_cast<uint32_t>(-1), 2, 5, 6,
- 982983, 888, static_cast<uint32_t>(-999), 65535, 0xFFFFFFFF, 0x80000000};
+static const MachineType all_reps[] = {kRepBit, kRepWord32, kRepWord64,
+ kRepFloat32, kRepFloat64, kRepTagged};
TEST(BoolToBit_constant) {
@@ -142,24 +137,234 @@
TEST(ToTagged_constant) {
RepresentationChangerTester r;
- for (size_t i = 0; i < arraysize(double_inputs); i++) {
- Node* n = r.jsgraph()->Float64Constant(double_inputs[i]);
- Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepTagged);
- r.CheckNumberConstant(c, double_inputs[i]);
+ {
+ FOR_FLOAT64_INPUTS(i) {
+ Node* n = r.jsgraph()->Float64Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepTagged);
+ r.CheckNumberConstant(c, *i);
+ }
}
- for (size_t i = 0; i < arraysize(int32_inputs); i++) {
- Node* n = r.jsgraph()->Int32Constant(int32_inputs[i]);
- Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
- kRepTagged);
- r.CheckNumberConstant(c, static_cast<double>(int32_inputs[i]));
+ {
+ FOR_FLOAT64_INPUTS(i) {
+ Node* n = r.jsgraph()->Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepTagged);
+ r.CheckNumberConstant(c, *i);
+ }
}
- for (size_t i = 0; i < arraysize(uint32_inputs); i++) {
- Node* n = r.jsgraph()->Int32Constant(uint32_inputs[i]);
- Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
- kRepTagged);
- r.CheckNumberConstant(c, static_cast<double>(uint32_inputs[i]));
+ {
+ FOR_FLOAT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Float32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32, kRepTagged);
+ r.CheckNumberConstant(c, *i);
+ }
+ }
+
+ {
+ FOR_INT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
+ kRepTagged);
+ r.CheckNumberConstant(c, *i);
+ }
+ }
+
+ {
+ FOR_UINT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
+ kRepTagged);
+ r.CheckNumberConstant(c, *i);
+ }
+ }
+}
+
+
+TEST(ToFloat64_constant) {
+ RepresentationChangerTester r;
+
+ {
+ FOR_FLOAT64_INPUTS(i) {
+ Node* n = r.jsgraph()->Float64Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepFloat64);
+ CHECK_EQ(n, c);
+ }
+ }
+
+ {
+ FOR_FLOAT64_INPUTS(i) {
+ Node* n = r.jsgraph()->Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepTagged, kRepFloat64);
+ r.CheckFloat64Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_FLOAT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Float32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32, kRepFloat64);
+ r.CheckFloat64Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_INT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
+ kRepFloat64);
+ r.CheckFloat64Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_UINT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
+ kRepFloat64);
+ r.CheckFloat64Constant(c, *i);
+ }
+ }
+}
+
+
+static bool IsFloat32Int32(int32_t val) {
+ return val >= -(1 << 23) && val <= (1 << 23);
+}
+
+
+static bool IsFloat32Uint32(uint32_t val) { return val <= (1 << 23); }
+
+
+TEST(ToFloat32_constant) {
+ RepresentationChangerTester r;
+
+ {
+ FOR_FLOAT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Float32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32, kRepFloat32);
+ CHECK_EQ(n, c);
+ }
+ }
+
+ {
+ FOR_FLOAT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepTagged, kRepFloat32);
+ r.CheckFloat32Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_FLOAT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Float64Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepFloat32);
+ r.CheckFloat32Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_INT32_INPUTS(i) {
+ if (!IsFloat32Int32(*i)) continue;
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
+ kRepFloat32);
+ r.CheckFloat32Constant(c, static_cast<float>(*i));
+ }
+ }
+
+ {
+ FOR_UINT32_INPUTS(i) {
+ if (!IsFloat32Uint32(*i)) continue;
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
+ kRepFloat32);
+ r.CheckFloat32Constant(c, static_cast<float>(*i));
+ }
+ }
+}
+
+
+TEST(ToInt32_constant) {
+ RepresentationChangerTester r;
+
+ {
+ FOR_INT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
+ kRepWord32);
+ r.CheckInt32Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_INT32_INPUTS(i) {
+ if (!IsFloat32Int32(*i)) continue;
+ Node* n = r.jsgraph()->Float32Constant(static_cast<float>(*i));
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32 | kTypeInt32,
+ kRepWord32);
+ r.CheckInt32Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_INT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Float64Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64 | kTypeInt32,
+ kRepWord32);
+ r.CheckInt32Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_INT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepTagged | kTypeInt32,
+ kRepWord32);
+ r.CheckInt32Constant(c, *i);
+ }
+ }
+}
+
+
+TEST(ToUint32_constant) {
+ RepresentationChangerTester r;
+
+ {
+ FOR_UINT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Int32Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
+ kRepWord32);
+ r.CheckUint32Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_UINT32_INPUTS(i) {
+ if (!IsFloat32Uint32(*i)) continue;
+ Node* n = r.jsgraph()->Float32Constant(static_cast<float>(*i));
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat32 | kTypeUint32,
+ kRepWord32);
+ r.CheckUint32Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_UINT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Float64Constant(*i);
+ Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64 | kTypeUint32,
+ kRepWord32);
+ r.CheckUint32Constant(c, *i);
+ }
+ }
+
+ {
+ FOR_UINT32_INPUTS(i) {
+ Node* n = r.jsgraph()->Constant(static_cast<double>(*i));
+ Node* c = r.changer()->GetRepresentationFor(n, kRepTagged | kTypeUint32,
+ kRepWord32);
+ r.CheckUint32Constant(c, *i);
+ }
}
}
@@ -177,6 +382,23 @@
}
+static void CheckTwoChanges(IrOpcode::Value expected2,
+ IrOpcode::Value expected1, MachineTypeUnion from,
+ MachineTypeUnion to) {
+ RepresentationChangerTester r;
+
+ Node* n = r.Parameter();
+ Node* c1 = r.changer()->GetRepresentationFor(n, from, to);
+
+ CHECK_NE(c1, n);
+ CHECK_EQ(expected1, c1->opcode());
+ Node* c2 = c1->InputAt(0);
+ CHECK_NE(c2, n);
+ CHECK_EQ(expected2, c2->opcode());
+ CHECK_EQ(n, c2->InputAt(0));
+}
+
+
TEST(SingleChanges) {
CheckChange(IrOpcode::kChangeBoolToBit, kRepTagged, kRepBit);
CheckChange(IrOpcode::kChangeBitToBool, kRepBit, kRepTagged);
@@ -202,6 +424,28 @@
kRepWord32);
CheckChange(IrOpcode::kChangeFloat64ToUint32, kRepFloat64 | kTypeUint32,
kRepWord32);
+
+ CheckChange(IrOpcode::kTruncateFloat64ToFloat32, kRepFloat64, kRepFloat32);
+
+ // Int32,Uint32 <-> Float32 require two changes.
+ CheckTwoChanges(IrOpcode::kChangeInt32ToFloat64,
+ IrOpcode::kTruncateFloat64ToFloat32, kRepWord32 | kTypeInt32,
+ kRepFloat32);
+ CheckTwoChanges(IrOpcode::kChangeUint32ToFloat64,
+ IrOpcode::kTruncateFloat64ToFloat32, kRepWord32 | kTypeUint32,
+ kRepFloat32);
+ CheckTwoChanges(IrOpcode::kChangeFloat32ToFloat64,
+ IrOpcode::kChangeFloat64ToInt32, kRepFloat32 | kTypeInt32,
+ kRepWord32);
+ CheckTwoChanges(IrOpcode::kChangeFloat32ToFloat64,
+ IrOpcode::kChangeFloat64ToUint32, kRepFloat32 | kTypeUint32,
+ kRepWord32);
+
+ // Float32 <-> Tagged require two changes.
+ CheckTwoChanges(IrOpcode::kChangeFloat32ToFloat64,
+ IrOpcode::kChangeFloat64ToTagged, kRepFloat32, kRepTagged);
+ CheckTwoChanges(IrOpcode::kChangeTaggedToFloat64,
+ IrOpcode::kTruncateFloat64ToFloat32, kRepTagged, kRepFloat32);
}
@@ -215,6 +459,11 @@
kRepWord32 | kTypeUint32);
CheckChange(IrOpcode::kChangeInt32ToFloat64, kRepWord32, kRepFloat64);
CheckChange(IrOpcode::kChangeFloat64ToInt32, kRepFloat64, kRepWord32);
+
+ CheckTwoChanges(IrOpcode::kChangeInt32ToFloat64,
+ IrOpcode::kTruncateFloat64ToFloat32, kRepWord32, kRepFloat32);
+ CheckTwoChanges(IrOpcode::kChangeFloat32ToFloat64,
+ IrOpcode::kChangeFloat64ToInt32, kRepFloat32, kRepWord32);
}
@@ -295,11 +544,4 @@
r.CheckTypeError(all_reps[i] | all_reps[j], kRepTagged);
}
}
-
- // TODO(titzer): Float32 representation changes trigger type errors now.
- // Enforce current behavior to test all paths through representation changer.
- for (size_t i = 0; i < arraysize(all_reps); i++) {
- r.CheckTypeError(all_reps[i], kRepFloat32);
- r.CheckTypeError(kRepFloat32, all_reps[i]);
- }
}
diff --git a/test/cctest/compiler/test-run-inlining.cc b/test/cctest/compiler/test-run-inlining.cc
index ad82fec..19b96ba 100644
--- a/test/cctest/compiler/test-run-inlining.cc
+++ b/test/cctest/compiler/test-run-inlining.cc
@@ -25,7 +25,8 @@
frames_seen++;
it.Advance();
}
- CHECK_EQ(args[0]->ToInt32()->Value(), topmost->GetInlineCount());
+ CHECK_EQ(args[0]->ToInt32(args.GetIsolate())->Value(),
+ topmost->GetInlineCount());
}
@@ -37,16 +38,20 @@
}
+static uint32_t kInlineFlags = CompilationInfo::kInliningEnabled |
+ CompilationInfo::kContextSpecializing |
+ CompilationInfo::kTypingEnabled;
+
+
TEST(SimpleInlining) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function(){"
- "function foo(s) { AssertInlineCount(2); return s; };"
- "function bar(s, t) { return foo(s); };"
- "return bar;})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ " function foo(s) { AssertInlineCount(2); return s; };"
+ " function bar(s, t) { return foo(s); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(1), T.Val(1), T.Val(2));
@@ -57,13 +62,11 @@
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function(){"
- "function foo(s) { %DeoptimizeFunction(bar); return "
- "s; };"
- "function bar(s, t) { return foo(s); };"
- "return bar;})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ " function foo(s) { %DeoptimizeFunction(bar); return s; };"
+ " function bar(s, t) { return foo(s); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(1), T.Val(1), T.Val(2));
@@ -74,13 +77,11 @@
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "function foo(s) { AssertInlineCount(2); var x = 12; return s + x; };"
- "function bar(s, t) { return foo(s); };"
- "return bar;"
+ " function foo(s) { AssertInlineCount(2); var x = 12; return s + x; };"
+ " function bar(s, t) { return foo(s); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(13), T.Val(1), T.Val(2));
@@ -91,16 +92,14 @@
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "function foo(s) { "
- " AssertInlineCount(2); %DeoptimizeFunction(bar); var x = 12;"
- " return s + x;"
- "};"
- "function bar(s, t) { return foo(s); };"
- "return bar;"
+ " function foo(s) {"
+ " AssertInlineCount(2); %DeoptimizeFunction(bar); var x = 12;"
+ " return s + x;"
+ " };"
+ " function bar(s, t) { return foo(s); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(13), T.Val(1), T.Val(2));
@@ -111,14 +110,12 @@
FLAG_turbo_deoptimization = true;
FunctionTester T(
"var f = (function () {"
- "var x = 42;"
- "function bar(s) { return x + s; };"
- "return (function (s) { return bar(s); });"
+ " var x = 42;"
+ " function bar(s) { return x + s; };"
+ " return (function (s) { return bar(s); });"
"})();"
- "(function (s) { return f(s)})",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ "(function (s) { return f(s) })",
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
@@ -132,12 +129,10 @@
FunctionTester T(
"var x = 42;"
"(function () {"
- "function bar(s, t) { return eval(\"AssertInlineCount(1); x\") };"
- "return bar;"
+ " function bar(s, t) { return eval(\"AssertInlineCount(1); x\") };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42), T.Val("x"), T.undefined());
@@ -148,13 +143,11 @@
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = 42;"
- "function bar(s, t, u, v) { AssertInlineCount(2); return x + s; };"
- "return (function (s,t) { return bar(s); });"
+ " var x = 42;"
+ " function bar(s, t, u, v) { AssertInlineCount(2); return x + s; };"
+ " return (function (s,t) { return bar(s); });"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
@@ -165,16 +158,14 @@
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "function foo(s,t,u,v) { AssertInlineCount(2); %DeoptimizeFunction(bar); "
- "return baz(); };"
- "function bar() { return foo(11); };"
- "function baz() { return foo.arguments.length == 1 && "
- " foo.arguments[0] == 11 ; }"
- "return bar;"
+ " function foo(s,t,u,v) { AssertInlineCount(2);"
+ " %DeoptimizeFunction(bar); return baz(); };"
+ " function bar() { return foo(11); };"
+ " function baz() { return foo.arguments.length == 1 &&"
+ " foo.arguments[0] == 11; }"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.true_value(), T.Val(12), T.Val(14));
@@ -185,14 +176,12 @@
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = 42;"
- "function foo(s) { AssertInlineCount(2); return x + s; };"
- "function bar(s,t) { return foo(s,t,13); };"
- "return bar;"
+ " var x = 42;"
+ " function foo(s) { AssertInlineCount(2); return x + s; };"
+ " function bar(s,t) { return foo(s,t,13); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
@@ -203,18 +192,16 @@
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "function foo(s) { AssertInlineCount(2); %DeoptimizeFunction(bar); "
- "return baz(); };"
- "function bar() { return foo(13, 14, 15); };"
- "function baz() { return foo.arguments.length == 3 && "
- " foo.arguments[0] == 13 && "
- " foo.arguments[1] == 14 && "
- " foo.arguments[2] == 15; }"
- "return bar;"
+ " function foo(s) { AssertInlineCount(2); %DeoptimizeFunction(bar);"
+ " return baz(); };"
+ " function bar() { return foo(13, 14, 15); };"
+ " function baz() { return foo.arguments.length == 3 &&"
+ " foo.arguments[0] == 13 &&"
+ " foo.arguments[1] == 14 &&"
+ " foo.arguments[2] == 15; }"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.true_value(), T.Val(12), T.Val(14));
@@ -225,13 +212,11 @@
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = 42;"
- "function bar(s) { AssertInlineCount(2); return x + s; };"
- "return (function (s,t) { return bar(s) + bar(t); });"
+ " var x = 42;"
+ " function bar(s) { AssertInlineCount(2); return x + s; };"
+ " return (function (s,t) { return bar(s) + bar(t); });"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(2 * 42 + 12 + 4), T.Val(12), T.Val(4));
@@ -242,14 +227,12 @@
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = 42;"
- "function foo(s) { AssertInlineCount(2); return x + s; };"
- "function bar(s,t) { return foo(foo(s)); };"
- "return bar;"
+ " var x = 42;"
+ " function foo(s) { AssertInlineCount(2); return x + s; };"
+ " function bar(s,t) { return foo(foo(s)); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42 + 42 + 12), T.Val(12), T.Val(4));
@@ -260,15 +243,13 @@
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = 41;"
- "function foo(s) { AssertInlineCount(2); if (s % 2 == 0) {"
- " return x - s } else { return x + s; } };"
- "function bar(s,t) { return foo(foo(s)); };"
- "return bar;"
+ " var x = 41;"
+ " function foo(s) { AssertInlineCount(2); if (s % 2 == 0) {"
+ " return x - s } else { return x + s; } };"
+ " function bar(s,t) { return foo(foo(s)); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(-11), T.Val(11), T.Val(4));
@@ -279,34 +260,60 @@
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = 41;"
- "function foo(s,t) { AssertInlineCount(2); if (s % 2 == 0) {"
- " return x - s * t } else { return x + s * t; } };"
- "function bar(s,t) { return foo(foo(s, 3), 5); };"
- "return bar;"
+ " var x = 41;"
+ " function foo(s,t) { AssertInlineCount(2); if (s % 2 == 0) {"
+ " return x - s * t } else { return x + s * t; } };"
+ " function bar(s,t) { return foo(foo(s, 3), 5); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(-329), T.Val(11), T.Val(4));
}
-TEST(InlineLoop) {
+TEST(InlineLoopGuardedEmpty) {
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = 41;"
- "function foo(s) { AssertInlineCount(2); while (s > 0) {"
- " s = s - 1; }; return s; };"
- "function bar(s,t) { return foo(foo(s)); };"
- "return bar;"
+ " function foo(s) { AssertInlineCount(2); if (s) while (s); return s; };"
+ " function bar(s,t) { return foo(s); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.Val(0.0), T.Val(0.0), T.Val(4));
+}
+
+
+TEST(InlineLoopGuardedOnce) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ " function foo(s,t) { AssertInlineCount(2); if (t > 0) while (s > 0) {"
+ " s = s - 1; }; return s; };"
+ " function bar(s,t) { return foo(s,t); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.Val(0.0), T.Val(11), T.Val(4));
+}
+
+
+TEST(InlineLoopGuardedTwice) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ " function foo(s,t) { AssertInlineCount(2); if (t > 0) while (s > 0) {"
+ " s = s - 1; }; return s; };"
+ " function bar(s,t) { return foo(foo(s,t),t); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(0.0), T.Val(11), T.Val(4));
@@ -317,15 +324,13 @@
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = Object.create({}, { y: { value:42, writable:false } });"
- "function foo(s) { 'use strict';"
- " x.y = 9; };"
- "function bar(s,t) { return foo(s); };"
- "return bar;"
+ " var x = Object.create({}, { y: { value:42, writable:false } });"
+ " function foo(s) { 'use strict';"
+ " x.y = 9; };"
+ " function bar(s,t) { return foo(s); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckThrows(T.undefined(), T.undefined());
@@ -336,18 +341,100 @@
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function () {"
- "var x = Object.create({}, { y: { value:42, writable:false } });"
- "function foo(s) { x.y = 9; return x.y; };"
- "function bar(s,t) { \'use strict\'; return foo(s); };"
- "return bar;"
+ " var x = Object.create({}, { y: { value:42, writable:false } });"
+ " function foo(s) { x.y = 9; return x.y; };"
+ " function bar(s,t) { \'use strict\'; return foo(s); };"
+ " return bar;"
"})();",
- CompilationInfo::kInliningEnabled |
- CompilationInfo::kContextSpecializing |
- CompilationInfo::kTypingEnabled);
+ kInlineFlags);
InstallAssertInlineCountHelper(CcTest::isolate());
T.CheckCall(T.Val(42), T.undefined(), T.undefined());
}
+TEST(InlineIntrinsicIsSmi) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ " var x = 42;"
+ " function bar(s,t) { return %_IsSmi(x); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.true_value(), T.Val(12), T.Val(4));
+}
+
+
+TEST(InlineIntrinsicIsNonNegativeSmi) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ " var x = 42;"
+ " function bar(s,t) { return %_IsNonNegativeSmi(x); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.true_value(), T.Val(12), T.Val(4));
+}
+
+
+TEST(InlineIntrinsicIsArray) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ " var x = [1,2,3];"
+ " function bar(s,t) { return %_IsArray(x); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.true_value(), T.Val(12), T.Val(4));
+
+ FunctionTester T2(
+ "(function () {"
+ " var x = 32;"
+ " function bar(s,t) { return %_IsArray(x); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
+
+ T2.CheckCall(T.false_value(), T.Val(12), T.Val(4));
+
+ FunctionTester T3(
+ "(function () {"
+ " var x = bar;"
+ " function bar(s,t) { return %_IsArray(x); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
+
+ T3.CheckCall(T.false_value(), T.Val(12), T.Val(4));
+}
+
+
+TEST(InlineWithArguments) {
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T(
+ "(function () {"
+ " function foo(s,t,u) { AssertInlineCount(2);"
+ " return foo.arguments.length == 3 &&"
+ " foo.arguments[0] == 13 &&"
+ " foo.arguments[1] == 14 &&"
+ " foo.arguments[2] == 15;"
+ " }"
+ " function bar() { return foo(13, 14, 15); };"
+ " return bar;"
+ "})();",
+ kInlineFlags);
+
+ InstallAssertInlineCountHelper(CcTest::isolate());
+ T.CheckCall(T.true_value(), T.Val(12), T.Val(14));
+}
+
#endif // V8_TURBOFAN_TARGET
diff --git a/test/cctest/compiler/test-run-intrinsics.cc b/test/cctest/compiler/test-run-intrinsics.cc
index a1b5676..76cbb8f 100644
--- a/test/cctest/compiler/test-run-intrinsics.cc
+++ b/test/cctest/compiler/test-run-intrinsics.cc
@@ -8,10 +8,12 @@
using namespace v8::internal;
using namespace v8::internal::compiler;
-
+uint32_t flags = CompilationInfo::kInliningEnabled;
TEST(IsSmi) {
- FunctionTester T("(function(a) { return %_IsSmi(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_IsSmi(a); })", flags);
T.CheckTrue(T.Val(1));
T.CheckFalse(T.Val(1.1));
@@ -23,7 +25,9 @@
TEST(IsNonNegativeSmi) {
- FunctionTester T("(function(a) { return %_IsNonNegativeSmi(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_IsNonNegativeSmi(a); })", flags);
T.CheckTrue(T.Val(1));
T.CheckFalse(T.Val(1.1));
@@ -35,7 +39,9 @@
TEST(IsMinusZero) {
- FunctionTester T("(function(a) { return %_IsMinusZero(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_IsMinusZero(a); })", flags);
T.CheckFalse(T.Val(1));
T.CheckFalse(T.Val(1.1));
@@ -47,7 +53,9 @@
TEST(IsArray) {
- FunctionTester T("(function(a) { return %_IsArray(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_IsArray(a); })", flags);
T.CheckFalse(T.NewObject("(function() {})"));
T.CheckTrue(T.NewObject("([1])"));
@@ -61,7 +69,9 @@
TEST(IsObject) {
- FunctionTester T("(function(a) { return %_IsObject(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_IsObject(a); })", flags);
T.CheckFalse(T.NewObject("(function() {})"));
T.CheckTrue(T.NewObject("([1])"));
@@ -75,7 +85,9 @@
TEST(IsFunction) {
- FunctionTester T("(function(a) { return %_IsFunction(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_IsFunction(a); })", flags);
T.CheckTrue(T.NewObject("(function() {})"));
T.CheckFalse(T.NewObject("([1])"));
@@ -89,7 +101,9 @@
TEST(IsRegExp) {
- FunctionTester T("(function(a) { return %_IsRegExp(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_IsRegExp(a); })", flags);
T.CheckFalse(T.NewObject("(function() {})"));
T.CheckFalse(T.NewObject("([1])"));
@@ -103,7 +117,9 @@
TEST(ClassOf) {
- FunctionTester T("(function(a) { return %_ClassOf(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_ClassOf(a); })", flags);
T.CheckCall(T.Val("Function"), T.NewObject("(function() {})"));
T.CheckCall(T.Val("Array"), T.NewObject("([1])"));
@@ -117,7 +133,9 @@
TEST(ObjectEquals) {
- FunctionTester T("(function(a,b) { return %_ObjectEquals(a,b); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_ObjectEquals(a,b); })", flags);
CompileRun("var o = {}");
T.CheckTrue(T.NewObject("(o)"), T.NewObject("(o)"));
@@ -130,7 +148,9 @@
TEST(ValueOf) {
- FunctionTester T("(function(a) { return %_ValueOf(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_ValueOf(a); })", flags);
T.CheckCall(T.Val("a"), T.Val("a"));
T.CheckCall(T.Val("b"), T.NewObject("(new String('b'))"));
@@ -140,7 +160,9 @@
TEST(SetValueOf) {
- FunctionTester T("(function(a,b) { return %_SetValueOf(a,b); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_SetValueOf(a,b); })", flags);
T.CheckCall(T.Val("a"), T.NewObject("(new String)"), T.Val("a"));
T.CheckCall(T.Val(123), T.NewObject("(new Number)"), T.Val(123));
@@ -149,7 +171,9 @@
TEST(StringCharFromCode) {
- FunctionTester T("(function(a) { return %_StringCharFromCode(a); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a) { return %_StringCharFromCode(a); })", flags);
T.CheckCall(T.Val("a"), T.Val(97));
T.CheckCall(T.Val("\xE2\x9D\x8A"), T.Val(0x274A));
@@ -158,7 +182,9 @@
TEST(StringCharAt) {
- FunctionTester T("(function(a,b) { return %_StringCharAt(a,b); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_StringCharAt(a,b); })", flags);
T.CheckCall(T.Val("e"), T.Val("huge fan!"), T.Val(3));
T.CheckCall(T.Val("f"), T.Val("\xE2\x9D\x8A fan!"), T.Val(2));
@@ -167,7 +193,10 @@
TEST(StringCharCodeAt) {
- FunctionTester T("(function(a,b) { return %_StringCharCodeAt(a,b); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_StringCharCodeAt(a,b); })",
+ flags);
T.CheckCall(T.Val('e'), T.Val("huge fan!"), T.Val(3));
T.CheckCall(T.Val('f'), T.Val("\xE2\x9D\x8A fan!"), T.Val(2));
@@ -176,7 +205,9 @@
TEST(StringAdd) {
- FunctionTester T("(function(a,b) { return %_StringAdd(a,b); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_StringAdd(a,b); })", flags);
T.CheckCall(T.Val("aaabbb"), T.Val("aaa"), T.Val("bbb"));
T.CheckCall(T.Val("aaa"), T.Val("aaa"), T.Val(""));
@@ -185,7 +216,9 @@
TEST(StringSubString) {
- FunctionTester T("(function(a,b) { return %_SubString(a,b,b+3); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_SubString(a,b,b+3); })", flags);
T.CheckCall(T.Val("aaa"), T.Val("aaabbb"), T.Val(0.0));
T.CheckCall(T.Val("abb"), T.Val("aaabbb"), T.Val(2));
@@ -194,7 +227,9 @@
TEST(StringCompare) {
- FunctionTester T("(function(a,b) { return %_StringCompare(a,b); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_StringCompare(a,b); })", flags);
T.CheckCall(T.Val(-1), T.Val("aaa"), T.Val("bbb"));
T.CheckCall(T.Val(0.0), T.Val("bbb"), T.Val("bbb"));
@@ -203,7 +238,10 @@
TEST(CallFunction) {
- FunctionTester T("(function(a,b) { return %_CallFunction(a, 1, 2, 3, b); })");
+ FLAG_turbo_inlining_intrinsics = true;
+ FLAG_turbo_deoptimization = true;
+ FunctionTester T("(function(a,b) { return %_CallFunction(a, 1, 2, 3, b); })",
+ flags);
CompileRun("function f(a,b,c) { return a + b + c + this.d; }");
T.CheckCall(T.Val(129), T.NewObject("({d:123})"), T.NewObject("f"));
diff --git a/test/cctest/compiler/test-run-jsbranches.cc b/test/cctest/compiler/test-run-jsbranches.cc
index df2fcdc..7a4a0b3 100644
--- a/test/cctest/compiler/test-run-jsbranches.cc
+++ b/test/cctest/compiler/test-run-jsbranches.cc
@@ -280,3 +280,78 @@
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/cctest/compiler/test-run-machops.cc b/test/cctest/compiler/test-run-machops.cc
index 985e0f8..974d4ce 100644
--- a/test/cctest/compiler/test-run-machops.cc
+++ b/test/cctest/compiler/test-run-machops.cc
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <cmath>
#include <functional>
#include <limits>
#include "src/base/bits.h"
-#include "src/compiler/generic-node-inl.h"
+#include "src/codegen.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/codegen-tester.h"
#include "test/cctest/compiler/value-helper.h"
@@ -58,25 +59,25 @@
TEST(CodeGenInt32Binop) {
RawMachineAssemblerTester<void> m;
- const Operator* ops[] = {
+ const Operator* kOps[] = {
m.machine()->Word32And(), m.machine()->Word32Or(),
m.machine()->Word32Xor(), m.machine()->Word32Shl(),
m.machine()->Word32Shr(), m.machine()->Word32Sar(),
m.machine()->Word32Equal(), m.machine()->Int32Add(),
m.machine()->Int32Sub(), m.machine()->Int32Mul(),
- m.machine()->Int32Div(), m.machine()->Int32UDiv(),
- m.machine()->Int32Mod(), m.machine()->Int32UMod(),
+ m.machine()->Int32MulHigh(), m.machine()->Int32Div(),
+ m.machine()->Uint32Div(), m.machine()->Int32Mod(),
+ m.machine()->Uint32Mod(), m.machine()->Uint32MulHigh(),
m.machine()->Int32LessThan(), m.machine()->Int32LessThanOrEqual(),
- m.machine()->Uint32LessThan(), m.machine()->Uint32LessThanOrEqual(),
- NULL};
+ m.machine()->Uint32LessThan(), m.machine()->Uint32LessThanOrEqual()};
- for (int i = 0; ops[i] != NULL; i++) {
+ for (size_t i = 0; i < arraysize(kOps); ++i) {
for (int j = 0; j < 8; j++) {
for (int k = 0; k < 8; k++) {
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
Node* a = Int32Input(&m, j);
Node* b = Int32Input(&m, k);
- m.Return(m.NewNode(ops[i], a, b));
+ m.Return(m.NewNode(kOps[i], a, b));
m.GenerateCode();
}
}
@@ -127,51 +128,6 @@
}
-TEST(RunRedundantBranch1) {
- RawMachineAssemblerTester<int32_t> m;
- int constant = 944777;
-
- MLabel blocka;
- m.Branch(m.Int32Constant(0), &blocka, &blocka);
- m.Bind(&blocka);
- m.Return(m.Int32Constant(constant));
-
- CHECK_EQ(constant, m.Call());
-}
-
-
-TEST(RunRedundantBranch2) {
- RawMachineAssemblerTester<int32_t> m;
- int constant = 955777;
-
- MLabel blocka, blockb;
- m.Branch(m.Int32Constant(0), &blocka, &blocka);
- m.Bind(&blockb);
- m.Goto(&blocka);
- m.Bind(&blocka);
- m.Return(m.Int32Constant(constant));
-
- CHECK_EQ(constant, m.Call());
-}
-
-
-TEST(RunRedundantBranch3) {
- RawMachineAssemblerTester<int32_t> m;
- int constant = 966777;
-
- MLabel blocka, blockb, blockc;
- m.Branch(m.Int32Constant(0), &blocka, &blockc);
- m.Bind(&blocka);
- m.Branch(m.Int32Constant(0), &blockb, &blockb);
- m.Bind(&blockc);
- m.Goto(&blockb);
- m.Bind(&blockb);
- m.Return(m.Int32Constant(constant));
-
- CHECK_EQ(constant, m.Call());
-}
-
-
TEST(RunDiamond2) {
RawMachineAssemblerTester<int32_t> m;
@@ -571,6 +527,142 @@
}
+TEST(RunInt32AddAndWord32EqualP) {
+ {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Parameter(0),
+ m.Word32Equal(m.Parameter(1), m.Parameter(2))));
+ FOR_INT32_INPUTS(i) {
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j == *k));
+ CHECK_EQ(expected, m.Call(*i, *j, *k));
+ }
+ }
+ }
+ }
+ {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Word32Equal(m.Parameter(0), m.Parameter(1)),
+ m.Parameter(2)));
+ FOR_INT32_INPUTS(i) {
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>((*i == *j) + bit_cast<uint32_t>(*k));
+ CHECK_EQ(expected, m.Call(*i, *j, *k));
+ }
+ }
+ }
+ }
+}
+
+
+TEST(RunInt32AddAndWord32EqualImm) {
+ {
+ FOR_INT32_INPUTS(i) {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Int32Constant(*i),
+ m.Word32Equal(m.Parameter(0), m.Parameter(1))));
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j == *k));
+ CHECK_EQ(expected, m.Call(*j, *k));
+ }
+ }
+ }
+ }
+ {
+ FOR_INT32_INPUTS(i) {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Word32Equal(m.Int32Constant(*i), m.Parameter(0)),
+ m.Parameter(1)));
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>((*i == *j) + bit_cast<uint32_t>(*k));
+ CHECK_EQ(expected, m.Call(*j, *k));
+ }
+ }
+ }
+ }
+}
+
+
+TEST(RunInt32AddAndWord32NotEqualP) {
+ {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Parameter(0),
+ m.Word32NotEqual(m.Parameter(1), m.Parameter(2))));
+ FOR_INT32_INPUTS(i) {
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j != *k));
+ CHECK_EQ(expected, m.Call(*i, *j, *k));
+ }
+ }
+ }
+ }
+ {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Word32NotEqual(m.Parameter(0), m.Parameter(1)),
+ m.Parameter(2)));
+ FOR_INT32_INPUTS(i) {
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>((*i != *j) + bit_cast<uint32_t>(*k));
+ CHECK_EQ(expected, m.Call(*i, *j, *k));
+ }
+ }
+ }
+ }
+}
+
+
+TEST(RunInt32AddAndWord32NotEqualImm) {
+ {
+ FOR_INT32_INPUTS(i) {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Int32Constant(*i),
+ m.Word32NotEqual(m.Parameter(0), m.Parameter(1))));
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j != *k));
+ CHECK_EQ(expected, m.Call(*j, *k));
+ }
+ }
+ }
+ }
+ {
+ FOR_INT32_INPUTS(i) {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+ m.Return(m.Int32Add(m.Word32NotEqual(m.Int32Constant(*i), m.Parameter(0)),
+ m.Parameter(1)));
+ FOR_INT32_INPUTS(j) {
+ FOR_INT32_INPUTS(k) {
+ // Use uint32_t because signed overflow is UB in C.
+ int32_t const expected =
+ bit_cast<int32_t>((*i != *j) + bit_cast<uint32_t>(*k));
+ CHECK_EQ(expected, m.Call(*j, *k));
+ }
+ }
+ }
+ }
+}
+
+
TEST(RunInt32AddAndWord32SarP) {
{
RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32, kMachUint32);
@@ -1233,6 +1325,20 @@
}
+TEST(RunInt32MulHighP) {
+ RawMachineAssemblerTester<int32_t> m;
+ Int32BinopTester bt(&m);
+ bt.AddReturn(m.Int32MulHigh(bt.param0, bt.param1));
+ FOR_INT32_INPUTS(i) {
+ FOR_INT32_INPUTS(j) {
+ int32_t expected = static_cast<int32_t>(
+ (static_cast<int64_t>(*i) * static_cast<int64_t>(*j)) >> 32);
+ CHECK_EQ(expected, bt.call(*i, *j));
+ }
+ }
+}
+
+
TEST(RunInt32MulImm) {
{
FOR_UINT32_INPUTS(i) {
@@ -1259,6 +1365,22 @@
TEST(RunInt32MulAndInt32AddP) {
{
+ FOR_INT32_INPUTS(i) {
+ FOR_INT32_INPUTS(j) {
+ RawMachineAssemblerTester<int32_t> m(kMachInt32);
+ int32_t p0 = *i;
+ int32_t p1 = *j;
+ m.Return(m.Int32Add(m.Int32Constant(p0),
+ m.Int32Mul(m.Parameter(0), m.Int32Constant(p1))));
+ FOR_INT32_INPUTS(k) {
+ int32_t p2 = *k;
+ int expected = p0 + static_cast<int32_t>(p1 * p2);
+ CHECK_EQ(expected, m.Call(p2));
+ }
+ }
+ }
+ }
+ {
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
m.Return(
m.Int32Add(m.Parameter(0), m.Int32Mul(m.Parameter(1), m.Parameter(2))));
@@ -1347,6 +1469,20 @@
}
+TEST(RunUint32MulHighP) {
+ RawMachineAssemblerTester<int32_t> m;
+ Int32BinopTester bt(&m);
+ bt.AddReturn(m.Uint32MulHigh(bt.param0, bt.param1));
+ FOR_UINT32_INPUTS(i) {
+ FOR_UINT32_INPUTS(j) {
+ int32_t expected = bit_cast<int32_t>(static_cast<uint32_t>(
+ (static_cast<uint64_t>(*i) * static_cast<uint64_t>(*j)) >> 32));
+ CHECK_EQ(expected, bt.call(bit_cast<int32_t>(*i), bit_cast<int32_t>(*j)));
+ }
+ }
+}
+
+
TEST(RunInt32DivP) {
{
RawMachineAssemblerTester<int32_t> m;
@@ -1381,11 +1517,11 @@
}
-TEST(RunInt32UDivP) {
+TEST(RunUint32DivP) {
{
RawMachineAssemblerTester<int32_t> m;
Int32BinopTester bt(&m);
- bt.AddReturn(m.Int32UDiv(bt.param0, bt.param1));
+ bt.AddReturn(m.Uint32Div(bt.param0, bt.param1));
FOR_UINT32_INPUTS(i) {
FOR_UINT32_INPUTS(j) {
uint32_t p0 = *i;
@@ -1400,7 +1536,7 @@
{
RawMachineAssemblerTester<int32_t> m;
Int32BinopTester bt(&m);
- bt.AddReturn(m.Int32Add(bt.param0, m.Int32UDiv(bt.param0, bt.param1)));
+ bt.AddReturn(m.Int32Add(bt.param0, m.Uint32Div(bt.param0, bt.param1)));
FOR_UINT32_INPUTS(i) {
FOR_UINT32_INPUTS(j) {
uint32_t p0 = *i;
@@ -1449,11 +1585,11 @@
}
-TEST(RunInt32UModP) {
+TEST(RunUint32ModP) {
{
RawMachineAssemblerTester<int32_t> m;
Int32BinopTester bt(&m);
- bt.AddReturn(m.Int32UMod(bt.param0, bt.param1));
+ bt.AddReturn(m.Uint32Mod(bt.param0, bt.param1));
FOR_UINT32_INPUTS(i) {
FOR_UINT32_INPUTS(j) {
uint32_t p0 = *i;
@@ -1468,7 +1604,7 @@
{
RawMachineAssemblerTester<int32_t> m;
Int32BinopTester bt(&m);
- bt.AddReturn(m.Int32Add(bt.param0, m.Int32UMod(bt.param0, bt.param1)));
+ bt.AddReturn(m.Int32Add(bt.param0, m.Uint32Mod(bt.param0, bt.param1)));
FOR_UINT32_INPUTS(i) {
FOR_UINT32_INPUTS(j) {
uint32_t p0 = *i;
@@ -2658,22 +2794,23 @@
TEST(RunDeadInt32Binops) {
RawMachineAssemblerTester<int32_t> m;
- const Operator* ops[] = {
- m.machine()->Word32And(), m.machine()->Word32Or(),
- m.machine()->Word32Xor(), m.machine()->Word32Shl(),
- m.machine()->Word32Shr(), m.machine()->Word32Sar(),
- m.machine()->Word32Ror(), m.machine()->Word32Equal(),
- m.machine()->Int32Add(), m.machine()->Int32Sub(),
- m.machine()->Int32Mul(), m.machine()->Int32Div(),
- m.machine()->Int32UDiv(), m.machine()->Int32Mod(),
- m.machine()->Int32UMod(), m.machine()->Int32LessThan(),
- m.machine()->Int32LessThanOrEqual(), m.machine()->Uint32LessThan(),
- m.machine()->Uint32LessThanOrEqual(), NULL};
+ const Operator* kOps[] = {
+ m.machine()->Word32And(), m.machine()->Word32Or(),
+ m.machine()->Word32Xor(), m.machine()->Word32Shl(),
+ m.machine()->Word32Shr(), m.machine()->Word32Sar(),
+ m.machine()->Word32Ror(), m.machine()->Word32Equal(),
+ m.machine()->Int32Add(), m.machine()->Int32Sub(),
+ m.machine()->Int32Mul(), m.machine()->Int32MulHigh(),
+ m.machine()->Int32Div(), m.machine()->Uint32Div(),
+ m.machine()->Int32Mod(), m.machine()->Uint32Mod(),
+ m.machine()->Uint32MulHigh(), m.machine()->Int32LessThan(),
+ m.machine()->Int32LessThanOrEqual(), m.machine()->Uint32LessThan(),
+ m.machine()->Uint32LessThanOrEqual()};
- for (int i = 0; ops[i] != NULL; i++) {
+ for (size_t i = 0; i < arraysize(kOps); ++i) {
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
- int constant = 0x55555 + i;
- m.NewNode(ops[i], m.Parameter(0), m.Parameter(1));
+ int32_t constant = static_cast<int32_t>(0x55555 + i);
+ m.NewNode(kOps[i], m.Parameter(0), m.Parameter(1));
m.Return(m.Int32Constant(constant));
CHECK_EQ(constant, m.Call(1, 1));
@@ -3102,6 +3239,38 @@
}
+TEST(RunChangeUint32ToFloat64_spilled) {
+ RawMachineAssemblerTester<int32_t> m;
+ const int kNumInputs = 32;
+ int32_t magic = 0x786234;
+ uint32_t input[kNumInputs];
+ double result[kNumInputs];
+ Node* input_node[kNumInputs];
+
+ for (int i = 0; i < kNumInputs; i++) {
+ input_node[i] =
+ m.Load(kMachUint32, m.PointerConstant(&input), m.Int32Constant(i * 4));
+ }
+
+ for (int i = 0; i < kNumInputs; i++) {
+ m.Store(kMachFloat64, m.PointerConstant(&result), m.Int32Constant(i * 8),
+ m.ChangeUint32ToFloat64(input_node[i]));
+ }
+
+ m.Return(m.Int32Constant(magic));
+
+ for (int i = 0; i < kNumInputs; i++) {
+ input[i] = 100 + i;
+ }
+
+ CHECK_EQ(magic, m.Call());
+
+ for (int i = 0; i < kNumInputs; i++) {
+ CHECK_EQ(result[i], static_cast<double>(100 + i));
+ }
+}
+
+
TEST(RunChangeFloat64ToInt32_A) {
RawMachineAssemblerTester<int32_t> m;
int32_t magic = 0x786234;
@@ -3272,6 +3441,38 @@
}
+TEST(RunTruncateFloat64ToFloat32_spilled) {
+ RawMachineAssemblerTester<uint32_t> m;
+ const int kNumInputs = 32;
+ int32_t magic = 0x786234;
+ double input[kNumInputs];
+ float result[kNumInputs];
+ Node* input_node[kNumInputs];
+
+ for (int i = 0; i < kNumInputs; i++) {
+ input_node[i] =
+ m.Load(kMachFloat64, m.PointerConstant(&input), m.Int32Constant(i * 8));
+ }
+
+ for (int i = 0; i < kNumInputs; i++) {
+ m.Store(kMachFloat32, m.PointerConstant(&result), m.Int32Constant(i * 4),
+ m.TruncateFloat64ToFloat32(input_node[i]));
+ }
+
+ m.Return(m.Int32Constant(magic));
+
+ for (int i = 0; i < kNumInputs; i++) {
+ input[i] = 0.1 + i;
+ }
+
+ CHECK_EQ(magic, m.Call());
+
+ for (int i = 0; i < kNumInputs; i++) {
+ CHECK_EQ(result[i], DoubleToFloat32(input[i]));
+ }
+}
+
+
TEST(RunDeadChangeFloat64ToInt32) {
RawMachineAssemblerTester<int32_t> m;
const int magic = 0x88abcda4;
@@ -4242,4 +4443,249 @@
}
}
+
+TEST(RunChangeFloat32ToFloat64) {
+ double actual = 0.0f;
+ float expected = 0.0;
+ RawMachineAssemblerTester<int32_t> m;
+ m.StoreToPointer(
+ &actual, kMachFloat64,
+ m.ChangeFloat32ToFloat64(m.LoadFromPointer(&expected, kMachFloat32)));
+ m.Return(m.Int32Constant(0));
+ FOR_FLOAT32_INPUTS(i) {
+ expected = *i;
+ CHECK_EQ(0, m.Call());
+ CHECK_EQ(expected, actual);
+ }
+}
+
+
+TEST(RunChangeFloat32ToFloat64_spilled) {
+ RawMachineAssemblerTester<int32_t> m;
+ const int kNumInputs = 32;
+ int32_t magic = 0x786234;
+ float input[kNumInputs];
+ double result[kNumInputs];
+ Node* input_node[kNumInputs];
+
+ for (int i = 0; i < kNumInputs; i++) {
+ input_node[i] =
+ m.Load(kMachFloat32, m.PointerConstant(&input), m.Int32Constant(i * 4));
+ }
+
+ for (int i = 0; i < kNumInputs; i++) {
+ m.Store(kMachFloat64, m.PointerConstant(&result), m.Int32Constant(i * 8),
+ m.ChangeFloat32ToFloat64(input_node[i]));
+ }
+
+ m.Return(m.Int32Constant(magic));
+
+ for (int i = 0; i < kNumInputs; i++) {
+ input[i] = 100.9f + i;
+ }
+
+ CHECK_EQ(magic, m.Call());
+
+ for (int i = 0; i < kNumInputs; i++) {
+ CHECK_EQ(result[i], static_cast<double>(input[i]));
+ }
+}
+
+
+TEST(RunTruncateFloat64ToFloat32) {
+ float actual = 0.0f;
+ double input = 0.0;
+ RawMachineAssemblerTester<int32_t> m;
+ m.StoreToPointer(
+ &actual, kMachFloat32,
+ m.TruncateFloat64ToFloat32(m.LoadFromPointer(&input, kMachFloat64)));
+ m.Return(m.Int32Constant(0));
+ FOR_FLOAT64_INPUTS(i) {
+ input = *i;
+ volatile double expected = DoubleToFloat32(input);
+ CHECK_EQ(0, m.Call());
+ CHECK_EQ(expected, actual);
+ }
+}
+
+
+TEST(RunFloat32Constant) {
+ FOR_FLOAT32_INPUTS(i) {
+ float expected = *i;
+ float actual = *i;
+ RawMachineAssemblerTester<int32_t> m;
+ m.StoreToPointer(&actual, kMachFloat32, m.Float32Constant(expected));
+ m.Return(m.Int32Constant(0));
+ CHECK_EQ(0, m.Call());
+ CHECK_EQ(expected, actual);
+ }
+}
+
+
+static double two_30 = 1 << 30; // 2^30 is a smi boundary.
+static double two_52 = two_30 * (1 << 22); // 2^52 is a precision boundary.
+static double kValues[] = {0.1,
+ 0.2,
+ 0.49999999999999994,
+ 0.5,
+ 0.7,
+ 1.0 - std::numeric_limits<double>::epsilon(),
+ -0.1,
+ -0.49999999999999994,
+ -0.5,
+ -0.7,
+ 1.1,
+ 1.0 + std::numeric_limits<double>::epsilon(),
+ 1.5,
+ 1.7,
+ -1,
+ -1 + std::numeric_limits<double>::epsilon(),
+ -1 - std::numeric_limits<double>::epsilon(),
+ -1.1,
+ -1.5,
+ -1.7,
+ std::numeric_limits<double>::min(),
+ -std::numeric_limits<double>::min(),
+ std::numeric_limits<double>::max(),
+ -std::numeric_limits<double>::max(),
+ std::numeric_limits<double>::infinity(),
+ -std::numeric_limits<double>::infinity(),
+ two_30,
+ two_30 + 0.1,
+ two_30 + 0.5,
+ two_30 + 0.7,
+ two_30 - 1,
+ two_30 - 1 + 0.1,
+ two_30 - 1 + 0.5,
+ two_30 - 1 + 0.7,
+ -two_30,
+ -two_30 + 0.1,
+ -two_30 + 0.5,
+ -two_30 + 0.7,
+ -two_30 + 1,
+ -two_30 + 1 + 0.1,
+ -two_30 + 1 + 0.5,
+ -two_30 + 1 + 0.7,
+ two_52,
+ two_52 + 0.1,
+ two_52 + 0.5,
+ two_52 + 0.5,
+ two_52 + 0.7,
+ two_52 + 0.7,
+ two_52 - 1,
+ two_52 - 1 + 0.1,
+ two_52 - 1 + 0.5,
+ two_52 - 1 + 0.7,
+ -two_52,
+ -two_52 + 0.1,
+ -two_52 + 0.5,
+ -two_52 + 0.7,
+ -two_52 + 1,
+ -two_52 + 1 + 0.1,
+ -two_52 + 1 + 0.5,
+ -two_52 + 1 + 0.7,
+ two_30,
+ two_30 - 0.1,
+ two_30 - 0.5,
+ two_30 - 0.7,
+ two_30 - 1,
+ two_30 - 1 - 0.1,
+ two_30 - 1 - 0.5,
+ two_30 - 1 - 0.7,
+ -two_30,
+ -two_30 - 0.1,
+ -two_30 - 0.5,
+ -two_30 - 0.7,
+ -two_30 + 1,
+ -two_30 + 1 - 0.1,
+ -two_30 + 1 - 0.5,
+ -two_30 + 1 - 0.7,
+ two_52,
+ two_52 - 0.1,
+ two_52 - 0.5,
+ two_52 - 0.5,
+ two_52 - 0.7,
+ two_52 - 0.7,
+ two_52 - 1,
+ two_52 - 1 - 0.1,
+ two_52 - 1 - 0.5,
+ two_52 - 1 - 0.7,
+ -two_52,
+ -two_52 - 0.1,
+ -two_52 - 0.5,
+ -two_52 - 0.7,
+ -two_52 + 1,
+ -two_52 + 1 - 0.1,
+ -two_52 + 1 - 0.5,
+ -two_52 + 1 - 0.7};
+
+
+TEST(RunFloat64Floor) {
+ double input = -1.0;
+ double result = 0.0;
+ RawMachineAssemblerTester<int32_t> m;
+ if (!m.machine()->HasFloat64Floor()) return;
+ m.StoreToPointer(&result, kMachFloat64,
+ m.Float64Floor(m.LoadFromPointer(&input, kMachFloat64)));
+ m.Return(m.Int32Constant(0));
+ for (size_t i = 0; i < arraysize(kValues); ++i) {
+ input = kValues[i];
+ CHECK_EQ(0, m.Call());
+ double expected = std::floor(kValues[i]);
+ CHECK_EQ(expected, result);
+ }
+}
+
+
+TEST(RunFloat64Ceil) {
+ double input = -1.0;
+ double result = 0.0;
+ RawMachineAssemblerTester<int32_t> m;
+ if (!m.machine()->HasFloat64Ceil()) return;
+ m.StoreToPointer(&result, kMachFloat64,
+ m.Float64Ceil(m.LoadFromPointer(&input, kMachFloat64)));
+ m.Return(m.Int32Constant(0));
+ for (size_t i = 0; i < arraysize(kValues); ++i) {
+ input = kValues[i];
+ CHECK_EQ(0, m.Call());
+ double expected = std::ceil(kValues[i]);
+ CHECK_EQ(expected, result);
+ }
+}
+
+
+TEST(RunFloat64RoundTruncate) {
+ double input = -1.0;
+ double result = 0.0;
+ RawMachineAssemblerTester<int32_t> m;
+ if (!m.machine()->HasFloat64Ceil()) return;
+ m.StoreToPointer(
+ &result, kMachFloat64,
+ m.Float64RoundTruncate(m.LoadFromPointer(&input, kMachFloat64)));
+ m.Return(m.Int32Constant(0));
+ for (size_t i = 0; i < arraysize(kValues); ++i) {
+ input = kValues[i];
+ CHECK_EQ(0, m.Call());
+ double expected = trunc(kValues[i]);
+ CHECK_EQ(expected, result);
+ }
+}
+
+
+TEST(RunFloat64RoundTiesAway) {
+ double input = -1.0;
+ double result = 0.0;
+ RawMachineAssemblerTester<int32_t> m;
+ if (!m.machine()->HasFloat64RoundTiesAway()) return;
+ m.StoreToPointer(
+ &result, kMachFloat64,
+ m.Float64RoundTiesAway(m.LoadFromPointer(&input, kMachFloat64)));
+ m.Return(m.Int32Constant(0));
+ for (size_t i = 0; i < arraysize(kValues); ++i) {
+ input = kValues[i];
+ CHECK_EQ(0, m.Call());
+ double expected = round(kValues[i]);
+ CHECK_EQ(expected, result);
+ }
+}
#endif // V8_TURBOFAN_TARGET
diff --git a/test/cctest/compiler/test-run-stackcheck.cc b/test/cctest/compiler/test-run-stackcheck.cc
new file mode 100644
index 0000000..8c1664b
--- /dev/null
+++ b/test/cctest/compiler/test-run-stackcheck.cc
@@ -0,0 +1,18 @@
+// 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/compiler/function-tester.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+TEST(TerminateAtMethodEntry) {
+ FunctionTester T("(function(a,b) { return 23; })");
+
+ T.CheckCall(T.Val(23));
+ T.isolate->stack_guard()->RequestTerminateExecution();
+ T.CheckThrows(T.undefined(), T.undefined());
+}
diff --git a/test/cctest/compiler/test-schedule.cc b/test/cctest/compiler/test-schedule.cc
index 6c05c05..1eb3547 100644
--- a/test/cctest/compiler/test-schedule.cc
+++ b/test/cctest/compiler/test-schedule.cc
@@ -5,7 +5,6 @@
#include "src/v8.h"
#include "src/compiler/common-operator.h"
-#include "src/compiler/generic-node-inl.h"
#include "src/compiler/graph.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node.h"
@@ -16,26 +15,25 @@
using namespace v8::internal;
using namespace v8::internal::compiler;
-static SimpleOperator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
- 0, 0, "dummy");
+static Operator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
+ "dummy", 0, 0, 0, 0, 0, 0);
TEST(TestScheduleAllocation) {
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
CHECK_NE(NULL, schedule.start());
- CHECK_EQ(schedule.start(), *(schedule.all_blocks().begin()));
+ CHECK_EQ(schedule.start(), schedule.GetBlockById(BasicBlock::Id::FromInt(0)));
}
TEST(TestScheduleAddNode) {
HandleAndZoneScope scope;
+ Schedule schedule(scope.main_zone());
Graph graph(scope.main_zone());
Node* n0 = graph.NewNode(&dummy_operator);
Node* n1 = graph.NewNode(&dummy_operator);
- Schedule schedule(scope.main_zone());
-
BasicBlock* entry = schedule.start();
schedule.AddNode(entry, n0);
schedule.AddNode(entry, n1);
@@ -51,50 +49,49 @@
TEST(TestScheduleAddGoto) {
HandleAndZoneScope scope;
-
Schedule schedule(scope.main_zone());
+
BasicBlock* entry = schedule.start();
BasicBlock* next = schedule.NewBasicBlock();
schedule.AddGoto(entry, next);
- CHECK_EQ(0, entry->PredecessorCount());
- CHECK_EQ(1, entry->SuccessorCount());
+ CHECK_EQ(0, static_cast<int>(entry->PredecessorCount()));
+ CHECK_EQ(1, static_cast<int>(entry->SuccessorCount()));
CHECK_EQ(next, entry->SuccessorAt(0));
- CHECK_EQ(1, next->PredecessorCount());
+ CHECK_EQ(1, static_cast<int>(next->PredecessorCount()));
CHECK_EQ(entry, next->PredecessorAt(0));
- CHECK_EQ(0, next->SuccessorCount());
+ CHECK_EQ(0, static_cast<int>(next->SuccessorCount()));
}
TEST(TestScheduleAddBranch) {
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
-
- BasicBlock* entry = schedule.start();
- BasicBlock* tblock = schedule.NewBasicBlock();
- BasicBlock* fblock = schedule.NewBasicBlock();
-
Graph graph(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
Node* n0 = graph.NewNode(&dummy_operator);
Node* b = graph.NewNode(common.Branch(), n0);
+ BasicBlock* entry = schedule.start();
+ BasicBlock* tblock = schedule.NewBasicBlock();
+ BasicBlock* fblock = schedule.NewBasicBlock();
+
schedule.AddBranch(entry, b, tblock, fblock);
- CHECK_EQ(0, entry->PredecessorCount());
- CHECK_EQ(2, entry->SuccessorCount());
+ CHECK_EQ(0, static_cast<int>(entry->PredecessorCount()));
+ CHECK_EQ(2, static_cast<int>(entry->SuccessorCount()));
CHECK_EQ(tblock, entry->SuccessorAt(0));
CHECK_EQ(fblock, entry->SuccessorAt(1));
- CHECK_EQ(1, tblock->PredecessorCount());
+ CHECK_EQ(1, static_cast<int>(tblock->PredecessorCount()));
CHECK_EQ(entry, tblock->PredecessorAt(0));
- CHECK_EQ(0, tblock->SuccessorCount());
+ CHECK_EQ(0, static_cast<int>(tblock->SuccessorCount()));
- CHECK_EQ(1, fblock->PredecessorCount());
+ CHECK_EQ(1, static_cast<int>(fblock->PredecessorCount()));
CHECK_EQ(entry, fblock->PredecessorAt(0));
- CHECK_EQ(0, fblock->SuccessorCount());
+ CHECK_EQ(0, static_cast<int>(fblock->SuccessorCount()));
}
@@ -106,8 +103,8 @@
BasicBlock* entry = schedule.start();
schedule.AddReturn(entry, n0);
- CHECK_EQ(0, entry->PredecessorCount());
- CHECK_EQ(1, entry->SuccessorCount());
+ CHECK_EQ(0, static_cast<int>(entry->PredecessorCount()));
+ CHECK_EQ(1, static_cast<int>(entry->SuccessorCount()));
CHECK_EQ(schedule.end(), entry->SuccessorAt(0));
}
@@ -120,18 +117,53 @@
BasicBlock* entry = schedule.start();
schedule.AddThrow(entry, n0);
- CHECK_EQ(0, entry->PredecessorCount());
- CHECK_EQ(1, entry->SuccessorCount());
+ CHECK_EQ(0, static_cast<int>(entry->PredecessorCount()));
+ CHECK_EQ(1, static_cast<int>(entry->SuccessorCount()));
CHECK_EQ(schedule.end(), entry->SuccessorAt(0));
}
+TEST(TestScheduleInsertBranch) {
+ HandleAndZoneScope scope;
+ Schedule schedule(scope.main_zone());
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+ Node* n0 = graph.NewNode(&dummy_operator);
+ Node* n1 = graph.NewNode(&dummy_operator);
+ Node* b = graph.NewNode(common.Branch(), n1);
+
+ BasicBlock* entry = schedule.start();
+ BasicBlock* tblock = schedule.NewBasicBlock();
+ BasicBlock* fblock = schedule.NewBasicBlock();
+ BasicBlock* merge = schedule.NewBasicBlock();
+ schedule.AddReturn(entry, n0);
+ schedule.AddGoto(tblock, merge);
+ schedule.AddGoto(fblock, merge);
+
+ schedule.InsertBranch(entry, merge, b, tblock, fblock);
+
+ CHECK_EQ(0, static_cast<int>(entry->PredecessorCount()));
+ CHECK_EQ(2, static_cast<int>(entry->SuccessorCount()));
+ CHECK_EQ(tblock, entry->SuccessorAt(0));
+ CHECK_EQ(fblock, entry->SuccessorAt(1));
+
+ CHECK_EQ(2, static_cast<int>(merge->PredecessorCount()));
+ CHECK_EQ(1, static_cast<int>(merge->SuccessorCount()));
+ CHECK_EQ(schedule.end(), merge->SuccessorAt(0));
+
+ CHECK_EQ(1, static_cast<int>(schedule.end()->PredecessorCount()));
+ CHECK_EQ(0, static_cast<int>(schedule.end()->SuccessorCount()));
+ CHECK_EQ(merge, schedule.end()->PredecessorAt(0));
+}
+
+
TEST(BuildMulNodeGraph) {
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
Graph graph(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
- MachineOperatorBuilder machine;
+ // TODO(titzer): use test operators.
+ MachineOperatorBuilder machine(scope.main_zone());
Node* start = graph.NewNode(common.Start(0));
graph.SetStart(start);
diff --git a/test/cctest/compiler/test-scheduler.cc b/test/cctest/compiler/test-scheduler.cc
index cf33123..1b79ed5 100644
--- a/test/cctest/compiler/test-scheduler.cc
+++ b/test/cctest/compiler/test-scheduler.cc
@@ -3,31 +3,71 @@
// found in the LICENSE file.
#include "src/v8.h"
-#include "test/cctest/cctest.h"
+#include "src/compiler/access-builder.h"
#include "src/compiler/common-operator.h"
-#include "src/compiler/generic-node-inl.h"
-#include "src/compiler/generic-node.h"
#include "src/compiler/graph.h"
#include "src/compiler/graph-visualizer.h"
#include "src/compiler/js-operator.h"
-#include "src/compiler/machine-operator.h"
#include "src/compiler/node.h"
+#include "src/compiler/opcodes.h"
#include "src/compiler/operator.h"
#include "src/compiler/schedule.h"
#include "src/compiler/scheduler.h"
+#include "src/compiler/simplified-operator.h"
#include "src/compiler/verifier.h"
+#include "test/cctest/cctest.h"
using namespace v8::internal;
using namespace v8::internal::compiler;
+Operator kIntAdd(IrOpcode::kInt32Add, Operator::kPure, "Int32Add", 2, 0, 0, 1,
+ 0, 0);
+
// TODO(titzer): pull RPO tests out to their own file.
+static void CheckRPONumbers(BasicBlockVector* order, size_t expected,
+ bool loops_allowed) {
+ CHECK(expected == order->size());
+ for (int i = 0; i < static_cast<int>(order->size()); i++) {
+ CHECK(order->at(i)->rpo_number() == i);
+ if (!loops_allowed) {
+ CHECK_EQ(NULL, order->at(i)->loop_end());
+ CHECK_EQ(NULL, order->at(i)->loop_header());
+ }
+ }
+}
+
+
+static void CheckLoop(BasicBlockVector* order, BasicBlock** blocks,
+ int body_size) {
+ BasicBlock* header = blocks[0];
+ BasicBlock* end = header->loop_end();
+ CHECK_NE(NULL, end);
+ CHECK_GT(end->rpo_number(), 0);
+ CHECK_EQ(body_size, end->rpo_number() - header->rpo_number());
+ for (int i = 0; i < body_size; i++) {
+ CHECK_GE(blocks[i]->rpo_number(), header->rpo_number());
+ CHECK_LT(blocks[i]->rpo_number(), end->rpo_number());
+ CHECK(header->LoopContains(blocks[i]));
+ CHECK(header->IsLoopHeader() || blocks[i]->loop_header() == header);
+ }
+ if (header->rpo_number() > 0) {
+ CHECK_NE(order->at(header->rpo_number() - 1)->loop_header(), header);
+ }
+ if (end->rpo_number() < static_cast<int>(order->size())) {
+ CHECK_NE(order->at(end->rpo_number())->loop_header(), header);
+ }
+}
+
+
struct TestLoop {
int count;
BasicBlock** nodes;
BasicBlock* header() { return nodes[0]; }
BasicBlock* last() { return nodes[count - 1]; }
~TestLoop() { delete[] nodes; }
+
+ void Check(BasicBlockVector* order) { CheckLoop(order, nodes, count); }
};
@@ -37,36 +77,15 @@
loop->nodes = new BasicBlock* [count];
for (int i = 0; i < count; i++) {
loop->nodes[i] = schedule->NewBasicBlock();
- if (i > 0) schedule->AddSuccessor(loop->nodes[i - 1], loop->nodes[i]);
+ if (i > 0) {
+ schedule->AddSuccessorForTesting(loop->nodes[i - 1], loop->nodes[i]);
+ }
}
- schedule->AddSuccessor(loop->nodes[count - 1], loop->nodes[0]);
+ schedule->AddSuccessorForTesting(loop->nodes[count - 1], loop->nodes[0]);
return loop;
}
-static void CheckRPONumbers(BasicBlockVector* order, int expected,
- bool loops_allowed) {
- CHECK_EQ(expected, static_cast<int>(order->size()));
- for (int i = 0; i < static_cast<int>(order->size()); i++) {
- CHECK(order->at(i)->rpo_number_ == i);
- if (!loops_allowed) CHECK_LT(order->at(i)->loop_end_, 0);
- }
-}
-
-
-static void CheckLoopContains(BasicBlock** blocks, int body_size) {
- BasicBlock* header = blocks[0];
- CHECK_GT(header->loop_end_, 0);
- CHECK_EQ(body_size, (header->loop_end_ - header->rpo_number_));
- for (int i = 0; i < body_size; i++) {
- int num = blocks[i]->rpo_number_;
- CHECK(num >= header->rpo_number_ && num < header->loop_end_);
- CHECK(header->LoopContains(blocks[i]));
- CHECK(header->IsLoopHeader() || blocks[i]->loop_header_ == header);
- }
-}
-
-
static int GetScheduledNodeCount(Schedule* schedule) {
int node_count = 0;
for (BasicBlockVectorIter i = schedule->rpo_order()->begin();
@@ -76,7 +95,7 @@
++j) {
++node_count;
}
- BasicBlock::Control control = block->control_;
+ BasicBlock::Control control = block->control();
if (control != BasicBlock::kNone) {
++node_count;
}
@@ -91,11 +110,11 @@
os << AsDOT(*graph);
}
- Schedule* schedule = Scheduler::ComputeSchedule(graph);
+ Schedule* schedule = Scheduler::ComputeSchedule(graph->zone(), graph);
if (FLAG_trace_turbo_scheduler) {
OFStream os(stdout);
- os << *schedule << endl;
+ os << *schedule << std::endl;
}
ScheduleVerifier::Run(schedule);
CHECK_EQ(expected, GetScheduledNodeCount(schedule));
@@ -107,7 +126,8 @@
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 1, false);
CHECK_EQ(schedule.start(), order->at(0));
}
@@ -118,7 +138,8 @@
Schedule schedule(scope.main_zone());
schedule.AddGoto(schedule.start(), schedule.end());
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 2, false);
CHECK_EQ(schedule.start(), order->at(0));
CHECK_EQ(schedule.end(), order->at(1));
@@ -134,18 +155,18 @@
BasicBlock* last = schedule.start();
for (int j = 0; j < i; j++) {
BasicBlock* block = schedule.NewBasicBlock();
+ block->set_deferred(i & 1);
schedule.AddGoto(last, block);
last = block;
}
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 1 + i, false);
- Schedule::BasicBlocks blocks(schedule.all_blocks());
- for (Schedule::BasicBlocks::iterator iter = blocks.begin();
- iter != blocks.end(); ++iter) {
- BasicBlock* block = *iter;
- if (block->rpo_number_ >= 0 && block->SuccessorCount() == 1) {
- CHECK(block->rpo_number_ + 1 == block->SuccessorAt(0)->rpo_number_);
+ for (size_t i = 0; i < schedule.BasicBlockCount(); i++) {
+ BasicBlock* block = schedule.GetBlockById(BasicBlock::Id::FromSize(i));
+ if (block->rpo_number() >= 0 && block->SuccessorCount() == 1) {
+ CHECK(block->rpo_number() + 1 == block->SuccessorAt(0)->rpo_number());
}
}
}
@@ -155,23 +176,26 @@
TEST(RPOSelfLoop) {
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
- schedule.AddSuccessor(schedule.start(), schedule.start());
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ schedule.AddSuccessorForTesting(schedule.start(), schedule.start());
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 1, true);
BasicBlock* loop[] = {schedule.start()};
- CheckLoopContains(loop, 1);
+ CheckLoop(order, loop, 1);
}
TEST(RPOEntryLoop) {
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
- schedule.AddSuccessor(schedule.start(), schedule.end());
- schedule.AddSuccessor(schedule.end(), schedule.start());
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlock* body = schedule.NewBasicBlock();
+ schedule.AddSuccessorForTesting(schedule.start(), body);
+ schedule.AddSuccessorForTesting(body, schedule.start());
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 2, true);
- BasicBlock* loop[] = {schedule.start(), schedule.end()};
- CheckLoopContains(loop, 2);
+ BasicBlock* loop[] = {schedule.start(), body};
+ CheckLoop(order, loop, 2);
}
@@ -179,10 +203,11 @@
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
SmartPointer<TestLoop> loop1(CreateLoop(&schedule, 2));
- schedule.AddSuccessor(schedule.start(), loop1->header());
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ schedule.AddSuccessorForTesting(schedule.start(), loop1->header());
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 3, true);
- CheckLoopContains(loop1->nodes, loop1->count);
+ loop1->Check(order);
}
@@ -190,11 +215,12 @@
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
SmartPointer<TestLoop> loop1(CreateLoop(&schedule, 2));
- schedule.AddSuccessor(schedule.start(), loop1->header());
- schedule.AddSuccessor(loop1->last(), schedule.start());
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ schedule.AddSuccessorForTesting(schedule.start(), loop1->header());
+ schedule.AddSuccessorForTesting(loop1->last(), schedule.start());
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 3, true);
- CheckLoopContains(loop1->nodes, loop1->count);
+ loop1->Check(order);
}
@@ -207,18 +233,19 @@
BasicBlock* C = schedule.NewBasicBlock();
BasicBlock* D = schedule.end();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(A, C);
- schedule.AddSuccessor(B, D);
- schedule.AddSuccessor(C, D);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(A, C);
+ schedule.AddSuccessorForTesting(B, D);
+ schedule.AddSuccessorForTesting(C, D);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 4, false);
- CHECK_EQ(0, A->rpo_number_);
- CHECK((B->rpo_number_ == 1 && C->rpo_number_ == 2) ||
- (B->rpo_number_ == 2 && C->rpo_number_ == 1));
- CHECK_EQ(3, D->rpo_number_);
+ CHECK_EQ(0, A->rpo_number());
+ CHECK((B->rpo_number() == 1 && C->rpo_number() == 2) ||
+ (B->rpo_number() == 2 && C->rpo_number() == 1));
+ CHECK_EQ(3, D->rpo_number());
}
@@ -231,15 +258,16 @@
BasicBlock* C = schedule.NewBasicBlock();
BasicBlock* D = schedule.end();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(B, C);
- schedule.AddSuccessor(C, B);
- schedule.AddSuccessor(C, D);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(B, C);
+ schedule.AddSuccessorForTesting(C, B);
+ schedule.AddSuccessorForTesting(C, D);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 4, true);
BasicBlock* loop[] = {B, C};
- CheckLoopContains(loop, 2);
+ CheckLoop(order, loop, 2);
}
@@ -252,15 +280,16 @@
BasicBlock* C = schedule.NewBasicBlock();
BasicBlock* D = schedule.end();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(B, C);
- schedule.AddSuccessor(C, B);
- schedule.AddSuccessor(B, D);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(B, C);
+ schedule.AddSuccessorForTesting(C, B);
+ schedule.AddSuccessorForTesting(B, D);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 4, true);
BasicBlock* loop[] = {B, C};
- CheckLoopContains(loop, 2);
+ CheckLoop(order, loop, 2);
}
@@ -277,32 +306,33 @@
BasicBlock* F = schedule.NewBasicBlock();
BasicBlock* G = schedule.end();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(B, C);
- schedule.AddSuccessor(C, D);
- schedule.AddSuccessor(D, E);
- schedule.AddSuccessor(E, F);
- schedule.AddSuccessor(F, B);
- schedule.AddSuccessor(B, G);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(B, C);
+ schedule.AddSuccessorForTesting(C, D);
+ schedule.AddSuccessorForTesting(D, E);
+ schedule.AddSuccessorForTesting(E, F);
+ schedule.AddSuccessorForTesting(F, B);
+ schedule.AddSuccessorForTesting(B, G);
// Throw in extra backedges from time to time.
- if (i == 1) schedule.AddSuccessor(B, B);
- if (i == 2) schedule.AddSuccessor(C, B);
- if (i == 3) schedule.AddSuccessor(D, B);
- if (i == 4) schedule.AddSuccessor(E, B);
- if (i == 5) schedule.AddSuccessor(F, B);
+ if (i == 1) schedule.AddSuccessorForTesting(B, B);
+ if (i == 2) schedule.AddSuccessorForTesting(C, B);
+ if (i == 3) schedule.AddSuccessorForTesting(D, B);
+ if (i == 4) schedule.AddSuccessorForTesting(E, B);
+ if (i == 5) schedule.AddSuccessorForTesting(F, B);
// Throw in extra loop exits from time to time.
- if (i == 6) schedule.AddSuccessor(B, G);
- if (i == 7) schedule.AddSuccessor(C, G);
- if (i == 8) schedule.AddSuccessor(D, G);
- if (i == 9) schedule.AddSuccessor(E, G);
- if (i == 10) schedule.AddSuccessor(F, G);
+ if (i == 6) schedule.AddSuccessorForTesting(B, G);
+ if (i == 7) schedule.AddSuccessorForTesting(C, G);
+ if (i == 8) schedule.AddSuccessorForTesting(D, G);
+ if (i == 9) schedule.AddSuccessorForTesting(E, G);
+ if (i == 10) schedule.AddSuccessorForTesting(F, G);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 7, true);
BasicBlock* loop[] = {B, C, D, E, F};
- CheckLoopContains(loop, 5);
+ CheckLoop(order, loop, 5);
}
}
@@ -318,21 +348,22 @@
BasicBlock* E = schedule.NewBasicBlock();
BasicBlock* F = schedule.end();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(B, C);
- schedule.AddSuccessor(C, D);
- schedule.AddSuccessor(D, C);
- schedule.AddSuccessor(D, E);
- schedule.AddSuccessor(E, B);
- schedule.AddSuccessor(E, F);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(B, C);
+ schedule.AddSuccessorForTesting(C, D);
+ schedule.AddSuccessorForTesting(D, C);
+ schedule.AddSuccessorForTesting(D, E);
+ schedule.AddSuccessorForTesting(E, B);
+ schedule.AddSuccessorForTesting(E, F);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 6, true);
BasicBlock* loop1[] = {B, C, D, E};
- CheckLoopContains(loop1, 4);
+ CheckLoop(order, loop1, 4);
BasicBlock* loop2[] = {C, D};
- CheckLoopContains(loop2, 2);
+ CheckLoop(order, loop2, 2);
}
@@ -349,28 +380,29 @@
BasicBlock* G = schedule.NewBasicBlock();
BasicBlock* H = schedule.end();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(B, C);
- schedule.AddSuccessor(C, D);
- schedule.AddSuccessor(D, E);
- schedule.AddSuccessor(E, F);
- schedule.AddSuccessor(F, G);
- schedule.AddSuccessor(G, H);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(B, C);
+ schedule.AddSuccessorForTesting(C, D);
+ schedule.AddSuccessorForTesting(D, E);
+ schedule.AddSuccessorForTesting(E, F);
+ schedule.AddSuccessorForTesting(F, G);
+ schedule.AddSuccessorForTesting(G, H);
- schedule.AddSuccessor(E, D);
- schedule.AddSuccessor(F, C);
- schedule.AddSuccessor(G, B);
+ schedule.AddSuccessorForTesting(E, D);
+ schedule.AddSuccessorForTesting(F, C);
+ schedule.AddSuccessorForTesting(G, B);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 8, true);
BasicBlock* loop1[] = {B, C, D, E, F, G};
- CheckLoopContains(loop1, 6);
+ CheckLoop(order, loop1, 6);
BasicBlock* loop2[] = {C, D, E, F};
- CheckLoopContains(loop2, 4);
+ CheckLoop(order, loop2, 4);
BasicBlock* loop3[] = {D, E};
- CheckLoopContains(loop3, 2);
+ CheckLoop(order, loop3, 2);
}
@@ -384,17 +416,18 @@
BasicBlock* A = schedule.start();
BasicBlock* E = schedule.end();
- schedule.AddSuccessor(A, loop1->header());
- schedule.AddSuccessor(loop1->header(), loop2->header());
- schedule.AddSuccessor(loop2->last(), E);
+ schedule.AddSuccessorForTesting(A, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->header(), loop2->header());
+ schedule.AddSuccessorForTesting(loop2->last(), E);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
- CheckLoopContains(loop1->nodes, loop1->count);
+ CHECK_EQ(static_cast<int>(schedule.BasicBlockCount()),
+ static_cast<int>(order->size()));
- CHECK_EQ(schedule.BasicBlockCount(), static_cast<int>(order->size()));
- CheckLoopContains(loop1->nodes, loop1->count);
- CheckLoopContains(loop2->nodes, loop2->count);
+ loop1->Check(order);
+ loop2->Check(order);
}
@@ -409,18 +442,18 @@
BasicBlock* S = schedule.NewBasicBlock();
BasicBlock* E = schedule.end();
- schedule.AddSuccessor(A, loop1->header());
- schedule.AddSuccessor(loop1->header(), S);
- schedule.AddSuccessor(S, loop2->header());
- schedule.AddSuccessor(loop2->last(), E);
+ schedule.AddSuccessorForTesting(A, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->header(), S);
+ schedule.AddSuccessorForTesting(S, loop2->header());
+ schedule.AddSuccessorForTesting(loop2->last(), E);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
- CheckLoopContains(loop1->nodes, loop1->count);
-
- CHECK_EQ(schedule.BasicBlockCount(), static_cast<int>(order->size()));
- CheckLoopContains(loop1->nodes, loop1->count);
- CheckLoopContains(loop2->nodes, loop2->count);
+ CHECK_EQ(static_cast<int>(schedule.BasicBlockCount()),
+ static_cast<int>(order->size()));
+ loop1->Check(order);
+ loop2->Check(order);
}
@@ -435,15 +468,16 @@
BasicBlock* A = schedule.start();
BasicBlock* E = schedule.end();
- schedule.AddSuccessor(A, loop1->header());
- schedule.AddSuccessor(loop1->nodes[exit], loop2->header());
- schedule.AddSuccessor(loop2->nodes[exit], E);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
- CheckLoopContains(loop1->nodes, loop1->count);
+ schedule.AddSuccessorForTesting(A, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->nodes[exit], loop2->header());
+ schedule.AddSuccessorForTesting(loop2->nodes[exit], E);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
- CHECK_EQ(schedule.BasicBlockCount(), static_cast<int>(order->size()));
- CheckLoopContains(loop1->nodes, loop1->count);
- CheckLoopContains(loop2->nodes, loop2->count);
+ CHECK_EQ(static_cast<int>(schedule.BasicBlockCount()),
+ static_cast<int>(order->size()));
+ loop1->Check(order);
+ loop2->Check(order);
}
}
}
@@ -461,23 +495,23 @@
BasicBlock* C = schedule.NewBasicBlock();
BasicBlock* E = schedule.end();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(B, loop1->header());
- schedule.AddSuccessor(loop1->header(), loop2->header());
- schedule.AddSuccessor(loop2->last(), C);
- schedule.AddSuccessor(C, E);
- schedule.AddSuccessor(C, B);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(B, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->header(), loop2->header());
+ schedule.AddSuccessorForTesting(loop2->last(), C);
+ schedule.AddSuccessorForTesting(C, E);
+ schedule.AddSuccessorForTesting(C, B);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
- CheckLoopContains(loop1->nodes, loop1->count);
-
- CHECK_EQ(schedule.BasicBlockCount(), static_cast<int>(order->size()));
- CheckLoopContains(loop1->nodes, loop1->count);
- CheckLoopContains(loop2->nodes, loop2->count);
+ CHECK_EQ(static_cast<int>(schedule.BasicBlockCount()),
+ static_cast<int>(order->size()));
+ loop1->Check(order);
+ loop2->Check(order);
BasicBlock* loop3[] = {B, loop1->nodes[0], loop2->nodes[0], C};
- CheckLoopContains(loop3, 4);
+ CheckLoop(order, loop3, 4);
}
@@ -492,15 +526,16 @@
BasicBlock* E = schedule.end();
SmartPointer<TestLoop> loop1(CreateLoop(&schedule, size));
- schedule.AddSuccessor(A, loop1->header());
- schedule.AddSuccessor(loop1->last(), E);
+ schedule.AddSuccessorForTesting(A, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->last(), E);
- schedule.AddSuccessor(loop1->nodes[i], loop1->header());
- schedule.AddSuccessor(loop1->nodes[j], E);
+ schedule.AddSuccessorForTesting(loop1->nodes[i], loop1->header());
+ schedule.AddSuccessorForTesting(loop1->nodes[j], E);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, schedule.BasicBlockCount(), true);
- CheckLoopContains(loop1->nodes, loop1->count);
+ loop1->Check(order);
}
}
}
@@ -518,16 +553,17 @@
BasicBlock* E = schedule.end();
SmartPointer<TestLoop> loop1(CreateLoop(&schedule, size));
- schedule.AddSuccessor(A, loop1->header());
- schedule.AddSuccessor(loop1->last(), E);
+ schedule.AddSuccessorForTesting(A, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->last(), E);
- schedule.AddSuccessor(loop1->nodes[i], loop1->header());
- schedule.AddSuccessor(loop1->nodes[j], D);
- schedule.AddSuccessor(D, E);
+ schedule.AddSuccessorForTesting(loop1->nodes[i], loop1->header());
+ schedule.AddSuccessorForTesting(loop1->nodes[j], D);
+ schedule.AddSuccessorForTesting(D, E);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, schedule.BasicBlockCount(), true);
- CheckLoopContains(loop1->nodes, loop1->count);
+ loop1->Check(order);
}
}
}
@@ -543,18 +579,19 @@
BasicBlock* E = schedule.end();
SmartPointer<TestLoop> loop1(CreateLoop(&schedule, size));
- schedule.AddSuccessor(A, loop1->header());
- schedule.AddSuccessor(loop1->last(), E);
+ schedule.AddSuccessorForTesting(A, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->last(), E);
for (int j = 0; j < size; j++) {
BasicBlock* O = schedule.NewBasicBlock();
- schedule.AddSuccessor(loop1->nodes[j], O);
- schedule.AddSuccessor(O, E);
+ schedule.AddSuccessorForTesting(loop1->nodes[j], O);
+ schedule.AddSuccessorForTesting(O, E);
}
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, schedule.BasicBlockCount(), true);
- CheckLoopContains(loop1->nodes, loop1->count);
+ loop1->Check(order);
}
}
@@ -568,22 +605,23 @@
BasicBlock* A = schedule.start();
BasicBlock* E = schedule.end();
SmartPointer<TestLoop> loop1(CreateLoop(&schedule, size));
- schedule.AddSuccessor(A, loop1->header());
- schedule.AddSuccessor(loop1->last(), E);
+ schedule.AddSuccessorForTesting(A, loop1->header());
+ schedule.AddSuccessorForTesting(loop1->last(), E);
TestLoop** loopN = new TestLoop* [size];
for (int j = 0; j < size; j++) {
loopN[j] = CreateLoop(&schedule, 2);
- schedule.AddSuccessor(loop1->nodes[j], loopN[j]->header());
- schedule.AddSuccessor(loopN[j]->last(), E);
+ schedule.AddSuccessorForTesting(loop1->nodes[j], loopN[j]->header());
+ schedule.AddSuccessorForTesting(loopN[j]->last(), E);
}
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, schedule.BasicBlockCount(), true);
- CheckLoopContains(loop1->nodes, loop1->count);
+ loop1->Check(order);
for (int j = 0; j < size; j++) {
- CheckLoopContains(loopN[j]->nodes, loopN[j]->count);
+ loopN[j]->Check(order);
delete loopN[j];
}
delete[] loopN;
@@ -598,22 +636,23 @@
BasicBlock* A = schedule.start();
BasicBlock* B = schedule.NewBasicBlock();
BasicBlock* C = schedule.NewBasicBlock();
- BasicBlock* D = schedule.end();
+ BasicBlock* D = schedule.NewBasicBlock();
BasicBlock* E = schedule.NewBasicBlock();
- schedule.AddSuccessor(A, B);
- schedule.AddSuccessor(B, C);
- schedule.AddSuccessor(B, D);
- schedule.AddSuccessor(B, E);
- schedule.AddSuccessor(C, B);
- schedule.AddSuccessor(D, B);
- schedule.AddSuccessor(E, B);
+ schedule.AddSuccessorForTesting(A, B);
+ schedule.AddSuccessorForTesting(B, C);
+ schedule.AddSuccessorForTesting(B, D);
+ schedule.AddSuccessorForTesting(B, E);
+ schedule.AddSuccessorForTesting(C, B);
+ schedule.AddSuccessorForTesting(D, B);
+ schedule.AddSuccessorForTesting(E, B);
- BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+ BasicBlockVector* order =
+ Scheduler::ComputeSpecialRPO(scope.main_zone(), &schedule);
CheckRPONumbers(order, 5, true);
BasicBlock* loop1[] = {B, C, D, E};
- CheckLoopContains(loop1, 4);
+ CheckLoop(order, loop1, 4);
}
@@ -624,7 +663,7 @@
graph.SetStart(graph.NewNode(builder.Start(0)));
graph.SetEnd(graph.NewNode(builder.End(), graph.start()));
- USE(Scheduler::ComputeSchedule(&graph));
+ USE(Scheduler::ComputeSchedule(scope.main_zone(), &graph));
}
@@ -639,7 +678,7 @@
graph.SetEnd(graph.NewNode(builder.End(), ret));
- USE(Scheduler::ComputeSchedule(&graph));
+ USE(Scheduler::ComputeSchedule(scope.main_zone(), &graph));
}
@@ -678,9 +717,10 @@
JSOperatorBuilder js_builder(scope.main_zone());
const Operator* op;
- Handle<Object> object =
- Handle<Object>(isolate->heap()->undefined_value(), isolate);
- Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+ Handle<HeapObject> object =
+ Handle<HeapObject>(isolate->heap()->undefined_value(), isolate);
+ Unique<HeapObject> unique_constant =
+ Unique<HeapObject>::CreateUninitialized(object);
// Manually transcripted code for:
// function turbo_fan_test(a, b, c, y) {
@@ -823,9 +863,10 @@
JSOperatorBuilder js_builder(scope.main_zone());
const Operator* op;
- Handle<Object> object =
- Handle<Object>(isolate->heap()->undefined_value(), isolate);
- Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+ Handle<HeapObject> object =
+ Handle<HeapObject>(isolate->heap()->undefined_value(), isolate);
+ Unique<HeapObject> unique_constant =
+ Unique<HeapObject>::CreateUninitialized(object);
// Manually transcripted code for:
// function turbo_fan_test(a, b) {
@@ -935,9 +976,10 @@
JSOperatorBuilder js_builder(scope.main_zone());
const Operator* op;
- Handle<Object> object =
- Handle<Object>(isolate->heap()->undefined_value(), isolate);
- Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+ Handle<HeapObject> object =
+ Handle<HeapObject>(isolate->heap()->undefined_value(), isolate);
+ Unique<HeapObject> unique_constant =
+ Unique<HeapObject>::CreateUninitialized(object);
// Manually transcripted code for:
// function turbo_fan_test(a, b, c) {
@@ -1182,9 +1224,10 @@
JSOperatorBuilder js_builder(scope.main_zone());
const Operator* op;
- Handle<Object> object =
- Handle<Object>(isolate->heap()->undefined_value(), isolate);
- Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+ Handle<HeapObject> object =
+ Handle<HeapObject>(isolate->heap()->undefined_value(), isolate);
+ Unique<HeapObject> unique_constant =
+ Unique<HeapObject>::CreateUninitialized(object);
// Manually transcripted code for:
// function turbo_fan_test(a, b, c) {
@@ -1509,12 +1552,12 @@
Graph graph(scope.main_zone());
CommonOperatorBuilder common_builder(scope.main_zone());
JSOperatorBuilder js_builder(scope.main_zone());
- MachineOperatorBuilder machine_builder;
const Operator* op;
- Handle<Object> object =
- Handle<Object>(isolate->heap()->undefined_value(), isolate);
- Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+ Handle<HeapObject> object =
+ Handle<HeapObject>(isolate->heap()->undefined_value(), isolate);
+ Unique<HeapObject> unique_constant =
+ Unique<HeapObject>::CreateUninitialized(object);
// Manually transcripted code for:
// function turbo_fan_test(a, b, c) {
@@ -1544,7 +1587,7 @@
Node* n20 = graph.NewNode(op, nil, nil, nil, nil, nil);
USE(n20);
n20->ReplaceInput(0, n9);
- op = machine_builder.Int32Add();
+ op = &kIntAdd;
Node* n19 = graph.NewNode(op, nil, nil);
USE(n19);
op = common_builder.Phi(kMachAnyTagged, 2);
@@ -1668,7 +1711,6 @@
HandleAndZoneScope scope;
Graph graph(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
- MachineOperatorBuilder machine;
Node* start = graph.NewNode(common.Start(2));
graph.SetStart(start);
@@ -1677,7 +1719,7 @@
Node* p1 = graph.NewNode(common.Parameter(1), start);
Node* d1 = CreateDiamond(&graph, &common, p0);
Node* d2 = CreateDiamond(&graph, &common, p1);
- Node* add = graph.NewNode(machine.Int32Add(), d1, d2);
+ Node* add = graph.NewNode(&kIntAdd, d1, d2);
Node* ret = graph.NewNode(common.Return(), add, start, start);
Node* end = graph.NewNode(common.End(), ret, start);
@@ -1691,7 +1733,6 @@
HandleAndZoneScope scope;
Graph graph(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
- MachineOperatorBuilder machine;
Node* start = graph.NewNode(common.Start(2));
graph.SetStart(start);
@@ -1700,7 +1741,7 @@
Node* p1 = graph.NewNode(common.Parameter(1), start);
Node* d1 = CreateDiamond(&graph, &common, p0);
Node* d2 = CreateDiamond(&graph, &common, p1);
- Node* add = graph.NewNode(machine.Int32Add(), d1, d2);
+ Node* add = graph.NewNode(&kIntAdd, d1, d2);
Node* d3 = CreateDiamond(&graph, &common, add);
Node* ret = graph.NewNode(common.Return(), d3, start, start);
Node* end = graph.NewNode(common.End(), ret, start);
@@ -1710,4 +1751,374 @@
ComputeAndVerifySchedule(33, &graph);
}
+
+TEST(NestedFloatingDiamonds) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+ SimplifiedOperatorBuilder simplified(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(2));
+ graph.SetStart(start);
+
+ Node* p0 = graph.NewNode(common.Parameter(0), start);
+
+ Node* fv = 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, 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(kMachAnyTagged, 2), ttrue, ffalse, m1);
+
+
+ Node* m = graph.NewNode(common.Merge(2), t, f);
+ Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 2), fv, phi1, m);
+ Node* ephi1 = graph.NewNode(common.EffectPhi(2), start, map, m);
+
+ Node* ret = graph.NewNode(common.Return(), phi, ephi1, start);
+ Node* end = graph.NewNode(common.End(), ret, start);
+
+ graph.SetEnd(end);
+
+ ComputeAndVerifySchedule(23, &graph);
+}
+
+
+TEST(NestedFloatingDiamondWithChain) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(2));
+ graph.SetStart(start);
+
+ Node* p0 = graph.NewNode(common.Parameter(0), start);
+ Node* p1 = graph.NewNode(common.Parameter(1), start);
+ Node* c = graph.NewNode(common.Int32Constant(7));
+
+ Node* brA1 = graph.NewNode(common.Branch(), p0, graph.start());
+ Node* tA1 = graph.NewNode(common.IfTrue(), brA1);
+ Node* fA1 = graph.NewNode(common.IfFalse(), brA1);
+ Node* mA1 = graph.NewNode(common.Merge(2), tA1, fA1);
+ Node* phiA1 = graph.NewNode(common.Phi(kMachAnyTagged, 2), p0, p1, mA1);
+
+ Node* brB1 = graph.NewNode(common.Branch(), p1, graph.start());
+ Node* tB1 = graph.NewNode(common.IfTrue(), brB1);
+ Node* fB1 = graph.NewNode(common.IfFalse(), brB1);
+ Node* mB1 = graph.NewNode(common.Merge(2), tB1, fB1);
+ Node* phiB1 = graph.NewNode(common.Phi(kMachAnyTagged, 2), p0, p1, mB1);
+
+ Node* brA2 = graph.NewNode(common.Branch(), phiB1, mA1);
+ Node* tA2 = graph.NewNode(common.IfTrue(), brA2);
+ Node* fA2 = graph.NewNode(common.IfFalse(), brA2);
+ Node* mA2 = graph.NewNode(common.Merge(2), tA2, fA2);
+ Node* phiA2 = graph.NewNode(common.Phi(kMachAnyTagged, 2), phiB1, c, mA2);
+
+ Node* brB2 = graph.NewNode(common.Branch(), phiA1, mB1);
+ Node* tB2 = graph.NewNode(common.IfTrue(), brB2);
+ Node* fB2 = graph.NewNode(common.IfFalse(), brB2);
+ Node* mB2 = graph.NewNode(common.Merge(2), tB2, fB2);
+ Node* phiB2 = graph.NewNode(common.Phi(kMachAnyTagged, 2), phiA1, c, mB2);
+
+ Node* add = graph.NewNode(&kIntAdd, phiA2, phiB2);
+ Node* ret = graph.NewNode(common.Return(), add, start, start);
+ Node* end = graph.NewNode(common.End(), ret, start);
+
+ graph.SetEnd(end);
+
+ ComputeAndVerifySchedule(35, &graph);
+}
+
+
+TEST(NestedFloatingDiamondWithLoop) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(2));
+ graph.SetStart(start);
+
+ Node* p0 = graph.NewNode(common.Parameter(0), start);
+
+ Node* fv = 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* loop = graph.NewNode(common.Loop(2), f, start);
+ Node* ind = graph.NewNode(common.Phi(kMachAnyTagged, 2), p0, p0, loop);
+
+ Node* add = graph.NewNode(&kIntAdd, ind, fv);
+ Node* br1 = graph.NewNode(common.Branch(), add, loop);
+ Node* t1 = graph.NewNode(common.IfTrue(), br1);
+ Node* f1 = graph.NewNode(common.IfFalse(), br1);
+
+ loop->ReplaceInput(1, t1); // close loop.
+ ind->ReplaceInput(1, ind); // close induction variable.
+
+ Node* m = graph.NewNode(common.Merge(2), t, f1);
+ Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 2), fv, ind, m);
+
+ Node* ret = graph.NewNode(common.Return(), phi, start, start);
+ Node* end = graph.NewNode(common.End(), ret, start);
+
+ graph.SetEnd(end);
+
+ ComputeAndVerifySchedule(20, &graph);
+}
+
+
+TEST(LoopedFloatingDiamond1) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ 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(kMachAnyTagged, 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(kMachAnyTagged, 2), add, p0, m1);
+
+ loop->ReplaceInput(1, t); // close loop.
+ ind->ReplaceInput(1, phi1); // close induction variable.
+
+ Node* ret = graph.NewNode(common.Return(), ind, start, f);
+ Node* end = graph.NewNode(common.End(), ret, f);
+
+ graph.SetEnd(end);
+
+ ComputeAndVerifySchedule(20, &graph);
+}
+
+
+TEST(LoopedFloatingDiamond2) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ 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(kMachAnyTagged, 2), p0, p0, loop);
+
+ 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(kMachAnyTagged, 2), c, ind, m1);
+
+ Node* add = graph.NewNode(&kIntAdd, ind, phi1);
+
+ Node* br = graph.NewNode(common.Branch(), add, loop);
+ Node* t = graph.NewNode(common.IfTrue(), br);
+ Node* f = graph.NewNode(common.IfFalse(), br);
+
+ loop->ReplaceInput(1, t); // close loop.
+ ind->ReplaceInput(1, add); // close induction variable.
+
+ Node* ret = graph.NewNode(common.Return(), ind, start, f);
+ Node* end = graph.NewNode(common.End(), ret, f);
+
+ graph.SetEnd(end);
+
+ ComputeAndVerifySchedule(20, &graph);
+}
+
+
+TEST(LoopedFloatingDiamond3) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ 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(kMachAnyTagged, 2), p0, p0, loop);
+
+ Node* br1 = graph.NewNode(common.Branch(), p0, graph.start());
+ Node* t1 = graph.NewNode(common.IfTrue(), br1);
+ Node* f1 = graph.NewNode(common.IfFalse(), br1);
+
+ Node* loop1 = graph.NewNode(common.Loop(2), t1, start);
+ Node* ind1 = graph.NewNode(common.Phi(kMachAnyTagged, 2), p0, p0, loop);
+
+ Node* add1 = graph.NewNode(&kIntAdd, ind1, c);
+ Node* br2 = graph.NewNode(common.Branch(), add1, loop1);
+ Node* t2 = graph.NewNode(common.IfTrue(), br2);
+ Node* f2 = graph.NewNode(common.IfFalse(), br2);
+
+ loop1->ReplaceInput(1, t2); // close inner loop.
+ ind1->ReplaceInput(1, ind1); // close inner induction variable.
+
+ Node* m1 = graph.NewNode(common.Merge(2), f1, f2);
+ Node* phi1 = graph.NewNode(common.Phi(kMachAnyTagged, 2), c, ind1, m1);
+
+ Node* add = graph.NewNode(&kIntAdd, ind, phi1);
+
+ Node* br = graph.NewNode(common.Branch(), add, loop);
+ Node* t = graph.NewNode(common.IfTrue(), br);
+ Node* f = graph.NewNode(common.IfFalse(), br);
+
+ loop->ReplaceInput(1, t); // close loop.
+ ind->ReplaceInput(1, add); // close induction variable.
+
+ Node* ret = graph.NewNode(common.Return(), ind, start, f);
+ Node* end = graph.NewNode(common.End(), ret, f);
+
+ graph.SetEnd(end);
+
+ ComputeAndVerifySchedule(28, &graph);
+}
+
+
+TEST(PhisPushedDownToDifferentBranches) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(2));
+ graph.SetStart(start);
+
+ Node* p0 = graph.NewNode(common.Parameter(0), start);
+ Node* p1 = graph.NewNode(common.Parameter(1), start);
+
+ Node* v1 = graph.NewNode(common.Int32Constant(1));
+ Node* v2 = graph.NewNode(common.Int32Constant(2));
+ Node* v3 = graph.NewNode(common.Int32Constant(3));
+ Node* v4 = graph.NewNode(common.Int32Constant(4));
+ Node* br = graph.NewNode(common.Branch(), p0, graph.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(kMachAnyTagged, 2), v1, v2, m);
+ Node* phi2 = graph.NewNode(common.Phi(kMachAnyTagged, 2), v3, v4, m);
+
+ Node* br2 = graph.NewNode(common.Branch(), p1, graph.start());
+ Node* t2 = graph.NewNode(common.IfTrue(), br2);
+ Node* f2 = graph.NewNode(common.IfFalse(), br2);
+ Node* m2 = graph.NewNode(common.Merge(2), t2, f2);
+ Node* phi3 = graph.NewNode(common.Phi(kMachAnyTagged, 2), phi, phi2, m2);
+
+ Node* ret = graph.NewNode(common.Return(), phi3, start, start);
+ Node* end = graph.NewNode(common.End(), ret, start);
+
+ graph.SetEnd(end);
+
+ ComputeAndVerifySchedule(24, &graph);
+}
+
+
+TEST(BranchHintTrue) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(1));
+ graph.SetStart(start);
+
+ Node* p0 = 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(BranchHint::kTrue), p0, 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(kMachAnyTagged, 2), tv, fv, m);
+ Node* ret = graph.NewNode(common.Return(), phi, start, start);
+ Node* end = graph.NewNode(common.End(), ret, start);
+
+ graph.SetEnd(end);
+
+ Schedule* schedule = ComputeAndVerifySchedule(13, &graph);
+ // Make sure the false block is marked as deferred.
+ CHECK(!schedule->block(t)->deferred());
+ CHECK(schedule->block(f)->deferred());
+}
+
+
+TEST(BranchHintFalse) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(1));
+ graph.SetStart(start);
+
+ Node* p0 = 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(BranchHint::kFalse), p0, 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(kMachAnyTagged, 2), tv, fv, m);
+ Node* ret = graph.NewNode(common.Return(), phi, start, start);
+ Node* end = graph.NewNode(common.End(), ret, start);
+
+ graph.SetEnd(end);
+
+ Schedule* schedule = ComputeAndVerifySchedule(13, &graph);
+ // Make sure the true block is marked as deferred.
+ CHECK(schedule->block(t)->deferred());
+ CHECK(!schedule->block(f)->deferred());
+}
+
+
+TEST(ScheduleTerminate) {
+ HandleAndZoneScope scope;
+ Graph graph(scope.main_zone());
+ CommonOperatorBuilder common(scope.main_zone());
+
+ Node* start = graph.NewNode(common.Start(1));
+ graph.SetStart(start);
+
+ Node* loop = graph.NewNode(common.Loop(2), start, start);
+ loop->ReplaceInput(1, loop); // self loop, NTL.
+
+ Node* effect = graph.NewNode(common.EffectPhi(1), start, loop);
+ effect->ReplaceInput(0, effect);
+
+ Node* terminate = graph.NewNode(common.Terminate(1), effect, loop);
+ Node* end = graph.NewNode(common.End(), terminate);
+
+ graph.SetEnd(end);
+
+ Schedule* schedule = ComputeAndVerifySchedule(6, &graph);
+ BasicBlock* block = schedule->block(loop);
+ CHECK_NE(NULL, loop);
+ CHECK_EQ(block, schedule->block(effect));
+ CHECK_GE(block->rpo_number(), 0);
+}
+
#endif
diff --git a/test/cctest/compiler/test-simplified-lowering.cc b/test/cctest/compiler/test-simplified-lowering.cc
index 96fb965..147aa32 100644
--- a/test/cctest/compiler/test-simplified-lowering.cc
+++ b/test/cctest/compiler/test-simplified-lowering.cc
@@ -5,8 +5,9 @@
#include <limits>
#include "src/compiler/access-builder.h"
+#include "src/compiler/change-lowering.h"
#include "src/compiler/control-builders.h"
-#include "src/compiler/generic-node-inl.h"
+#include "src/compiler/graph-reducer.h"
#include "src/compiler/graph-visualizer.h"
#include "src/compiler/node-properties-inl.h"
#include "src/compiler/pipeline.h"
@@ -35,11 +36,10 @@
MachineType p3 = kMachNone,
MachineType p4 = kMachNone)
: GraphBuilderTester<ReturnType>(p0, p1, p2, p3, p4),
- typer(this->zone()),
+ typer(this->graph(), MaybeHandle<Context>()),
javascript(this->zone()),
- jsgraph(this->graph(), this->common(), &javascript, &typer,
- this->machine()),
- lowering(&jsgraph) {}
+ jsgraph(this->graph(), this->common(), &javascript, this->machine()),
+ lowering(&jsgraph, this->zone()) {}
Typer typer;
JSOperatorBuilder javascript;
@@ -48,16 +48,40 @@
void LowerAllNodes() {
this->End();
+ typer.Run();
lowering.LowerAllNodes();
}
+ void LowerAllNodesAndLowerChanges() {
+ this->End();
+ typer.Run();
+ lowering.LowerAllNodes();
+
+ Zone* zone = this->zone();
+ CompilationInfo info(zone->isolate(), zone);
+ Linkage linkage(
+ zone, Linkage::GetSimplifiedCDescriptor(zone, this->machine_sig_));
+ ChangeLowering lowering(&jsgraph, &linkage);
+ GraphReducer reducer(this->graph(), this->zone());
+ reducer.AddReducer(&lowering);
+ reducer.ReduceGraph();
+ Verifier::Run(this->graph());
+ }
+
+ void CheckNumberCall(double expected, double input) {
+ // TODO(titzer): make calls to NewNumber work in cctests.
+ if (expected <= Smi::kMinValue) return;
+ if (expected >= Smi::kMaxValue) return;
+ Handle<Object> num = factory()->NewNumber(input);
+ Object* result = this->Call(*num);
+ CHECK(factory()->NewNumber(expected)->SameValue(result));
+ }
+
Factory* factory() { return this->isolate()->factory(); }
Heap* heap() { return this->isolate()->heap(); }
};
-#ifndef V8_TARGET_ARCH_ARM64
-// TODO(titzer): these result in a stub call that doesn't work on ARM64.
// TODO(titzer): factor these tests out to test-run-simplifiedops.cc.
// TODO(titzer): test tagged representation for input to NumberToInt32.
TEST(RunNumberToInt32_float64) {
@@ -68,6 +92,7 @@
FieldAccess load = {kUntaggedBase, 0, Handle<Name>(), Type::Number(),
kMachFloat64};
Node* loaded = t.LoadField(load, t.PointerConstant(&input));
+ NodeProperties::SetBounds(loaded, Bounds(Type::Number()));
Node* convert = t.NumberToInt32(loaded);
FieldAccess store = {kUntaggedBase, 0, Handle<Name>(), Type::Signed32(),
kMachInt32};
@@ -96,6 +121,7 @@
FieldAccess load = {kUntaggedBase, 0, Handle<Name>(), Type::Number(),
kMachFloat64};
Node* loaded = t.LoadField(load, t.PointerConstant(&input));
+ NodeProperties::SetBounds(loaded, Bounds(Type::Number()));
Node* convert = t.NumberToUint32(loaded);
FieldAccess store = {kUntaggedBase, 0, Handle<Name>(), Type::Unsigned32(),
kMachUint32};
@@ -113,7 +139,6 @@
}
}
}
-#endif
// Create a simple JSObject with a unique map.
@@ -207,10 +232,8 @@
TEST(RunLoadStoreFixedArrayIndex) {
SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
ElementAccess access = AccessBuilder::ForFixedArrayElement();
- Node* load = t.LoadElement(access, t.Parameter(0), t.Int32Constant(0),
- t.Int32Constant(2));
- t.StoreElement(access, t.Parameter(0), t.Int32Constant(1), t.Int32Constant(2),
- load);
+ Node* load = t.LoadElement(access, t.Parameter(0), t.Int32Constant(0));
+ t.StoreElement(access, t.Parameter(0), t.Int32Constant(1), load);
t.Return(load);
t.LowerAllNodes();
@@ -235,14 +258,13 @@
const int index = 12;
const int array_length = 2 * index;
ElementAccess buffer_access =
- AccessBuilder::ForBackingStoreElement(kMachInt8);
+ AccessBuilder::ForTypedArrayElement(v8::kExternalInt8Array, true);
Node* backing_store = t.LoadField(
AccessBuilder::ForJSArrayBufferBackingStore(), t.Parameter(0));
Node* load =
- t.LoadElement(buffer_access, backing_store, t.Int32Constant(index),
- t.Int32Constant(array_length));
+ t.LoadElement(buffer_access, backing_store, t.Int32Constant(index));
t.StoreElement(buffer_access, backing_store, t.Int32Constant(index + 1),
- t.Int32Constant(array_length), load);
+ load);
t.Return(t.jsgraph.TrueConstant());
t.LowerAllNodes();
@@ -329,9 +351,8 @@
kMachAnyTagged};
SimplifiedLoweringTester<Object*> t;
- Node* load = t.LoadElement(
- access, t.PointerConstant(smis), t.Int32Constant(static_cast<int>(j)),
- t.Int32Constant(static_cast<int>(arraysize(smis))));
+ Node* load = t.LoadElement(access, t.PointerConstant(smis),
+ t.Int32Constant(static_cast<int>(j)));
t.Return(load);
t.LowerAllNodes();
@@ -360,8 +381,7 @@
SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
Node* p0 = t.Parameter(0);
t.StoreElement(access, t.PointerConstant(smis),
- t.Int32Constant(static_cast<int>(j)),
- t.Int32Constant(static_cast<int>(arraysize(smis))), p0);
+ t.Int32Constant(static_cast<int>(j)), p0);
t.Return(p0);
t.LowerAllNodes();
@@ -427,10 +447,8 @@
SimplifiedLoweringTester<Object*> t;
Node* ptr = GetBaseNode(&t);
- Node* load = t.LoadElement(access, ptr, t.Int32Constant(from_index),
- t.Int32Constant(static_cast<int>(num_elements)));
- t.StoreElement(access, ptr, t.Int32Constant(to_index),
- t.Int32Constant(static_cast<int>(num_elements)), load);
+ Node* load = t.LoadElement(access, ptr, t.Int32Constant(from_index));
+ t.StoreElement(access, ptr, t.Int32Constant(to_index), load);
t.Return(t.jsgraph.TrueConstant());
t.LowerAllNodes();
t.GenerateCode();
@@ -648,9 +666,9 @@
explicit TestingGraph(Type* p0_type, Type* p1_type = Type::None(),
Type* p2_type = Type::None())
: GraphAndBuilders(main_zone()),
- typer(main_zone()),
+ typer(graph(), MaybeHandle<Context>()),
javascript(main_zone()),
- jsgraph(graph(), common(), &javascript, &typer, machine()) {
+ jsgraph(graph(), common(), &javascript, machine()) {
start = graph()->NewNode(common()->Start(2));
graph()->SetStart(start);
ret =
@@ -660,6 +678,7 @@
p0 = graph()->NewNode(common()->Parameter(0), start);
p1 = graph()->NewNode(common()->Parameter(1), start);
p2 = graph()->NewNode(common()->Parameter(2), start);
+ typer.Run();
NodeProperties::SetBounds(p0, Bounds(p0_type));
NodeProperties::SetBounds(p1, Bounds(p1_type));
NodeProperties::SetBounds(p2, Bounds(p2_type));
@@ -679,10 +698,7 @@
CHECK_EQ(expected, node->opcode());
}
- void Lower() {
- SimplifiedLowering lowering(&jsgraph);
- lowering.LowerAllNodes();
- }
+ void Lower() { SimplifiedLowering(&jsgraph, jsgraph.zone()).LowerAllNodes(); }
// Inserts the node as the return value of the graph.
Node* Return(Node* node) {
@@ -718,6 +734,17 @@
}
}
+ Node* ExampleWithTypeAndRep(Type* type, MachineType mach_type) {
+ FieldAccess access = {kUntaggedBase, 0, Handle<Name>::null(), type,
+ mach_type};
+ // TODO(titzer): using loads here just to force the representation is ugly.
+ Node* node = graph()->NewNode(simplified()->LoadField(access),
+ jsgraph.IntPtrConstant(0), graph()->start(),
+ graph()->start());
+ NodeProperties::SetBounds(node, Bounds(type));
+ return node;
+ }
+
Node* Use(Node* node, MachineType type) {
if (type & kTypeInt32) {
return graph()->NewNode(machine()->Int32LessThan(), node,
@@ -731,6 +758,9 @@
} else if (type & kRepWord64) {
return graph()->NewNode(machine()->Int64LessThan(), node,
Int64Constant(1));
+ } else if (type & kRepWord32) {
+ return graph()->NewNode(machine()->Word32Equal(), node,
+ jsgraph.Int32Constant(1));
} else {
return graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), node,
jsgraph.TrueConstant());
@@ -757,6 +787,50 @@
};
+TEST(LowerAnyToBoolean_bit_bit) {
+ // AnyToBoolean(x: kRepBit) used as kRepBit
+ HandleAndZoneScope scope;
+ Factory* f = scope.main_zone()->isolate()->factory();
+ Handle<Object> zero = f->NewNumber(0);
+ Handle<Object> one = f->NewNumber(1);
+ Type* singleton_zero = Type::Constant(zero, scope.main_zone());
+ Type* singleton_one = Type::Constant(one, scope.main_zone());
+ Type* zero_one_range = Type::Range(zero, one, scope.main_zone());
+ static Type* kTypes[] = {
+ singleton_zero, singleton_one, zero_one_range, Type::Boolean(),
+ Type::Union(Type::Boolean(), singleton_zero, scope.main_zone()),
+ Type::Union(Type::Boolean(), singleton_one, scope.main_zone()),
+ Type::Union(Type::Boolean(), zero_one_range, scope.main_zone())};
+ for (Type* type : kTypes) {
+ TestingGraph t(type);
+ Node* x = t.ExampleWithTypeAndRep(type, kRepBit);
+ Node* cnv = t.graph()->NewNode(t.simplified()->AnyToBoolean(), x);
+ Node* use = t.Branch(cnv);
+ t.Lower();
+ CHECK_EQ(x, use->InputAt(0));
+ }
+}
+
+
+#if V8_TURBOFAN_TARGET
+
+TEST(LowerAnyToBoolean_tagged_tagged) {
+ // AnyToBoolean(x: kRepTagged) used as kRepTagged
+ TestingGraph t(Type::Any());
+ Node* x = t.p0;
+ Node* cnv = t.graph()->NewNode(t.simplified()->AnyToBoolean(), x);
+ Node* use = t.Use(cnv, kRepTagged);
+ t.Return(use);
+ t.Lower();
+ CHECK_EQ(IrOpcode::kCall, cnv->opcode());
+ CHECK_EQ(IrOpcode::kHeapConstant, cnv->InputAt(0)->opcode());
+ CHECK_EQ(x, cnv->InputAt(1));
+ CHECK_EQ(t.jsgraph.NoContextConstant(), cnv->InputAt(2));
+}
+
+#endif
+
+
TEST(LowerBooleanNot_bit_bit) {
// BooleanNot(x: kRepBit) used as kRepBit
TestingGraph t(Type::Boolean());
@@ -765,7 +839,7 @@
Node* use = t.Branch(inv);
t.Lower();
Node* cmp = use->InputAt(0);
- CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode());
+ CHECK_EQ(t.machine()->Word32Equal()->opcode(), cmp->opcode());
CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1));
Node* f = t.jsgraph.Int32Constant(0);
CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1));
@@ -782,7 +856,7 @@
t.Lower();
CHECK_EQ(IrOpcode::kChangeBitToBool, use->InputAt(0)->opcode());
Node* cmp = use->InputAt(0)->InputAt(0);
- CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode());
+ CHECK_EQ(t.machine()->Word32Equal()->opcode(), cmp->opcode());
CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1));
Node* f = t.jsgraph.Int32Constant(0);
CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1));
@@ -921,24 +995,50 @@
TEST(LowerNumberAddSub_to_int32) {
- TestingGraph t(Type::Signed32(), Type::Signed32());
- t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Add,
- t.simplified()->NumberAdd(),
- t.simplified()->NumberToInt32());
- t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Sub,
- t.simplified()->NumberSubtract(),
- t.simplified()->NumberToInt32());
+ HandleAndZoneScope scope;
+ Factory* f = scope.main_zone()->isolate()->factory();
+ Type* small_range =
+ Type::Range(f->NewNumber(1), f->NewNumber(10), scope.main_zone());
+ Type* large_range =
+ Type::Range(f->NewNumber(-1e+13), f->NewNumber(1e+14), scope.main_zone());
+ static Type* types[] = {Type::Signed32(), Type::Integral32(), small_range,
+ large_range};
+
+ for (size_t i = 0; i < arraysize(types); i++) {
+ for (size_t j = 0; j < arraysize(types); j++) {
+ TestingGraph t(types[i], types[j]);
+ t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Add,
+ t.simplified()->NumberAdd(),
+ t.simplified()->NumberToInt32());
+ t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Sub,
+ t.simplified()->NumberSubtract(),
+ t.simplified()->NumberToInt32());
+ }
+ }
}
TEST(LowerNumberAddSub_to_uint32) {
- TestingGraph t(Type::Unsigned32(), Type::Unsigned32());
- t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Add,
- t.simplified()->NumberAdd(),
- t.simplified()->NumberToUint32());
- t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Sub,
- t.simplified()->NumberSubtract(),
- t.simplified()->NumberToUint32());
+ HandleAndZoneScope scope;
+ Factory* f = scope.main_zone()->isolate()->factory();
+ Type* small_range =
+ Type::Range(f->NewNumber(1), f->NewNumber(10), scope.main_zone());
+ Type* large_range =
+ Type::Range(f->NewNumber(-1e+13), f->NewNumber(1e+14), scope.main_zone());
+ static Type* types[] = {Type::Signed32(), Type::Integral32(), small_range,
+ large_range};
+
+ for (size_t i = 0; i < arraysize(types); i++) {
+ for (size_t j = 0; j < arraysize(types); j++) {
+ TestingGraph t(types[i], types[j]);
+ t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Add,
+ t.simplified()->NumberAdd(),
+ t.simplified()->NumberToUint32());
+ t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Sub,
+ t.simplified()->NumberSubtract(),
+ t.simplified()->NumberToUint32());
+ }
+ }
}
@@ -958,8 +1058,10 @@
TestingGraph t(test_types[i], test_types[i]);
t.CheckLoweringBinop(IrOpcode::kFloat64Div, t.simplified()->NumberDivide());
- t.CheckLoweringBinop(IrOpcode::kFloat64Mod,
- t.simplified()->NumberModulus());
+ if (!test_types[i]->Is(Type::Unsigned32())) {
+ t.CheckLoweringBinop(IrOpcode::kFloat64Mod,
+ t.simplified()->NumberModulus());
+ }
}
}
@@ -1006,7 +1108,7 @@
TEST(LowerNumberToInt32_to_TruncateFloat64ToInt32) {
// NumberToInt32(x: kRepFloat64) used as kMachInt32
TestingGraph t(Type::Number());
- Node* p0 = t.ExampleWithOutput(kMachFloat64);
+ Node* p0 = t.ExampleWithTypeAndRep(Type::Number(), kMachFloat64);
Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), p0);
Node* use = t.Use(trunc, kMachInt32);
t.Return(use);
@@ -1030,17 +1132,6 @@
}
-TEST(LowerNumberToInt32_to_ChangeFloat64ToTagged) {
- // TODO(titzer): NumberToInt32(x: kRepFloat64 | kTypeInt32) used as kRepTagged
-}
-
-
-TEST(LowerNumberToInt32_to_ChangeFloat64ToInt32) {
- // TODO(titzer): NumberToInt32(x: kRepFloat64 | kTypeInt32) used as kRepWord32
- // | kTypeInt32
-}
-
-
TEST(LowerNumberToUint32_to_nop) {
// NumberToUint32(x: kRepTagged | kTypeUint32) used as kRepTagged
TestingGraph t(Type::Unsigned32());
@@ -1078,6 +1169,8 @@
// NumberToUint32(x: kRepFloat64) used as kMachUint32
TestingGraph t(Type::Number());
Node* p0 = t.ExampleWithOutput(kMachFloat64);
+ // TODO(titzer): run the typer here, or attach machine type to param.
+ NodeProperties::SetBounds(p0, Bounds(Type::Number()));
Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), p0);
Node* use = t.Use(trunc, kMachUint32);
t.Return(use);
@@ -1101,20 +1194,67 @@
}
-TEST(LowerNumberToUint32_to_ChangeFloat64ToTagged) {
- // TODO(titzer): NumberToUint32(x: kRepFloat64 | kTypeUint32) used as
- // kRepTagged
+TEST(LowerNumberToUint32_to_TruncateFloat64ToInt32_uint32) {
+ // NumberToUint32(x: kRepFloat64) used as kRepWord32
+ TestingGraph t(Type::Unsigned32());
+ Node* input = t.ExampleWithTypeAndRep(Type::Number(), kMachFloat64);
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), input);
+ Node* use = t.Use(trunc, kRepWord32);
+ t.Return(use);
+ t.Lower();
+ CheckChangeOf(IrOpcode::kTruncateFloat64ToInt32, input, use->InputAt(0));
}
-TEST(LowerNumberToUint32_to_ChangeFloat64ToUint32) {
- // TODO(titzer): NumberToUint32(x: kRepFloat64 | kTypeUint32) used as
- // kRepWord32
+TEST(LowerNumberToUI32_of_Float64_used_as_word32) {
+ // NumberTo(Int,Uint)32(x: kRepFloat64 | kType(Int,Uint)32) used as
+ // kType(Int,Uint)32 | kRepWord32
+ Type* types[] = {Type::Signed32(), Type::Unsigned32()};
+ MachineType mach[] = {kTypeInt32, kTypeUint32, kMachNone};
+
+ for (int i = 0; i < 2; i++) {
+ for (int u = 0; u < 3; u++) {
+ TestingGraph t(types[i]);
+ Node* input = t.ExampleWithTypeAndRep(
+ types[i], static_cast<MachineType>(kRepFloat64 | mach[i]));
+ const Operator* op = i == 0 ? t.simplified()->NumberToInt32()
+ : t.simplified()->NumberToUint32();
+ Node* trunc = t.graph()->NewNode(op, input);
+ Node* use = t.Use(trunc, static_cast<MachineType>(kRepWord32 | mach[u]));
+ t.Return(use);
+ t.Lower();
+ IrOpcode::Value opcode = i == 0 ? IrOpcode::kChangeFloat64ToInt32
+ : IrOpcode::kChangeFloat64ToUint32;
+ CheckChangeOf(opcode, input, use->InputAt(0));
+ }
+ }
}
-TEST(LowerNumberToUint32_to_TruncateFloat64ToUint32) {
- // TODO(titzer): NumberToUint32(x: kRepFloat64) used as kRepWord32
+TEST(LowerNumberToUI32_of_Float64_used_as_tagged) {
+ // NumberTo(Int,Uint)32(x: kRepFloat64 | kType(Int,Uint)32) used as
+ // kType(Int,Uint)32 | kRepTagged
+ Type* types[] = {Type::Signed32(), Type::Unsigned32(), Type::Any()};
+ MachineType mach[] = {kTypeInt32, kTypeUint32, kMachNone};
+
+ for (int i = 0; i < 2; i++) {
+ for (int u = 0; u < 3; u++) {
+ TestingGraph t(types[i]);
+ Node* input = t.ExampleWithTypeAndRep(
+ types[i], static_cast<MachineType>(kRepFloat64 | mach[i]));
+ const Operator* op = i == 0 ? t.simplified()->NumberToInt32()
+ : t.simplified()->NumberToUint32();
+ Node* trunc = t.graph()->NewNode(op, input);
+ // TODO(titzer): we use the store here to force the representation.
+ FieldAccess access = {kTaggedBase, 0, Handle<Name>(), types[u],
+ static_cast<MachineType>(mach[u] | kRepTagged)};
+ Node* store = t.graph()->NewNode(t.simplified()->StoreField(access), t.p0,
+ trunc, t.start, t.start);
+ t.Effect(store);
+ t.Lower();
+ CheckChangeOf(IrOpcode::kChangeFloat64ToTagged, input, store->InputAt(2));
+ }
+ }
}
@@ -1268,45 +1408,54 @@
}
+namespace {
+
void CheckFieldAccessArithmetic(FieldAccess access, Node* load_or_store) {
- Int32Matcher index = Int32Matcher(load_or_store->InputAt(1));
- CHECK(index.Is(access.offset - access.tag()));
+ IntPtrMatcher mindex(load_or_store->InputAt(1));
+ CHECK(mindex.Is(access.offset - access.tag()));
}
Node* CheckElementAccessArithmetic(ElementAccess access, Node* load_or_store) {
- Int32BinopMatcher index(load_or_store->InputAt(1));
- CHECK_EQ(IrOpcode::kInt32Add, index.node()->opcode());
- CHECK(index.right().Is(access.header_size - access.tag()));
+ Node* index = load_or_store->InputAt(1);
+ if (kPointerSize == 8) {
+ CHECK_EQ(IrOpcode::kChangeUint32ToUint64, index->opcode());
+ index = index->InputAt(0);
+ }
- int element_size = ElementSizeOf(access.machine_type);
+ Int32BinopMatcher mindex(index);
+ CHECK_EQ(IrOpcode::kInt32Add, mindex.node()->opcode());
+ CHECK(mindex.right().Is(access.header_size - access.tag()));
- if (element_size != 1) {
- Int32BinopMatcher mul(index.left().node());
- CHECK_EQ(IrOpcode::kInt32Mul, mul.node()->opcode());
- CHECK(mul.right().Is(element_size));
- return mul.left().node();
+ const int element_size_shift = ElementSizeLog2Of(access.machine_type);
+ if (element_size_shift) {
+ Int32BinopMatcher shl(mindex.left().node());
+ CHECK_EQ(IrOpcode::kWord32Shl, shl.node()->opcode());
+ CHECK(shl.right().Is(element_size_shift));
+ return shl.left().node();
} else {
- return index.left().node();
+ return mindex.left().node();
}
}
-static const MachineType machine_reps[] = {
- kRepBit, kMachInt8, kMachInt16, kMachInt32,
- kMachInt64, kMachFloat64, kMachAnyTagged};
+const MachineType kMachineReps[] = {kRepBit, kMachInt8, kMachInt16,
+ kMachInt32, kMachInt64, kMachFloat64,
+ kMachAnyTagged};
+
+} // namespace
TEST(LowerLoadField_to_load) {
TestingGraph t(Type::Any(), Type::Signed32());
- for (size_t i = 0; i < arraysize(machine_reps); i++) {
+ for (size_t i = 0; i < arraysize(kMachineReps); i++) {
FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
- Handle<Name>::null(), Type::Any(), machine_reps[i]};
+ Handle<Name>::null(), Type::Any(), kMachineReps[i]};
Node* load =
t.graph()->NewNode(t.simplified()->LoadField(access), t.p0, t.start);
- Node* use = t.Use(load, machine_reps[i]);
+ Node* use = t.Use(load, kMachineReps[i]);
t.Return(use);
t.Lower();
CHECK_EQ(IrOpcode::kLoad, load->opcode());
@@ -1314,7 +1463,7 @@
CheckFieldAccessArithmetic(access, load);
MachineType rep = OpParameter<MachineType>(load);
- CHECK_EQ(machine_reps[i], rep);
+ CHECK_EQ(kMachineReps[i], rep);
}
}
@@ -1322,12 +1471,12 @@
TEST(LowerStoreField_to_store) {
TestingGraph t(Type::Any(), Type::Signed32());
- for (size_t i = 0; i < arraysize(machine_reps); i++) {
+ for (size_t i = 0; i < arraysize(kMachineReps); i++) {
FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
- Handle<Name>::null(), Type::Any(), machine_reps[i]};
+ Handle<Name>::null(), Type::Any(), kMachineReps[i]};
- Node* val = t.ExampleWithOutput(machine_reps[i]);
+ Node* val = t.ExampleWithOutput(kMachineReps[i]);
Node* store = t.graph()->NewNode(t.simplified()->StoreField(access), t.p0,
val, t.start, t.start);
t.Effect(store);
@@ -1337,10 +1486,10 @@
CheckFieldAccessArithmetic(access, store);
StoreRepresentation rep = OpParameter<StoreRepresentation>(store);
- if (machine_reps[i] & kRepTagged) {
+ if (kMachineReps[i] & kRepTagged) {
CHECK_EQ(kFullWriteBarrier, rep.write_barrier_kind());
}
- CHECK_EQ(machine_reps[i], rep.machine_type());
+ CHECK_EQ(kMachineReps[i], rep.machine_type());
}
}
@@ -1348,14 +1497,13 @@
TEST(LowerLoadElement_to_load) {
TestingGraph t(Type::Any(), Type::Signed32());
- for (size_t i = 0; i < arraysize(machine_reps); i++) {
+ for (size_t i = 0; i < arraysize(kMachineReps); i++) {
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
- Type::Any(), machine_reps[i]};
+ Type::Any(), kMachineReps[i]};
- Node* load =
- t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0, t.p1,
- t.jsgraph.Int32Constant(1024), t.start);
- Node* use = t.Use(load, machine_reps[i]);
+ Node* load = t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0,
+ t.p1, t.start, t.start);
+ Node* use = t.Use(load, kMachineReps[i]);
t.Return(use);
t.Lower();
CHECK_EQ(IrOpcode::kLoad, load->opcode());
@@ -1363,7 +1511,7 @@
CheckElementAccessArithmetic(access, load);
MachineType rep = OpParameter<MachineType>(load);
- CHECK_EQ(machine_reps[i], rep);
+ CHECK_EQ(kMachineReps[i], rep);
}
}
@@ -1371,14 +1519,13 @@
TEST(LowerStoreElement_to_store) {
TestingGraph t(Type::Any(), Type::Signed32());
- for (size_t i = 0; i < arraysize(machine_reps); i++) {
+ for (size_t i = 0; i < arraysize(kMachineReps); i++) {
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
- Type::Any(), machine_reps[i]};
+ Type::Any(), kMachineReps[i]};
- Node* val = t.ExampleWithOutput(machine_reps[i]);
+ Node* val = t.ExampleWithOutput(kMachineReps[i]);
Node* store = t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0,
- t.p1, t.jsgraph.Int32Constant(1024), val,
- t.start, t.start);
+ t.p1, val, t.start, t.start);
t.Effect(store);
t.Lower();
CHECK_EQ(IrOpcode::kStore, store->opcode());
@@ -1386,10 +1533,10 @@
CheckElementAccessArithmetic(access, store);
StoreRepresentation rep = OpParameter<StoreRepresentation>(store);
- if (machine_reps[i] & kRepTagged) {
+ if (kMachineReps[i] & kRepTagged) {
CHECK_EQ(kFullWriteBarrier, rep.write_barrier_kind());
}
- CHECK_EQ(machine_reps[i], rep.machine_type());
+ CHECK_EQ(kMachineReps[i], rep.machine_type());
}
}
@@ -1397,12 +1544,12 @@
TEST(InsertChangeForLoadElementIndex) {
// LoadElement(obj: Tagged, index: kTypeInt32 | kRepTagged, length) =>
// Load(obj, Int32Add(Int32Mul(ChangeTaggedToInt32(index), #k), #k))
- TestingGraph t(Type::Any(), Type::Signed32(), Type::Any());
+ TestingGraph t(Type::Any(), Type::Signed32());
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(),
kMachAnyTagged};
Node* load = t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0,
- t.p1, t.p2, t.start);
+ t.p1, t.start, t.start);
t.Return(load);
t.Lower();
CHECK_EQ(IrOpcode::kLoad, load->opcode());
@@ -1416,12 +1563,12 @@
TEST(InsertChangeForStoreElementIndex) {
// StoreElement(obj: Tagged, index: kTypeInt32 | kRepTagged, length, val) =>
// Store(obj, Int32Add(Int32Mul(ChangeTaggedToInt32(index), #k), #k), val)
- TestingGraph t(Type::Any(), Type::Signed32(), Type::Any());
+ TestingGraph t(Type::Any(), Type::Signed32());
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(),
kMachAnyTagged};
Node* store =
- t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0, t.p1, t.p2,
+ t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0, t.p1,
t.jsgraph.TrueConstant(), t.start, t.start);
t.Effect(store);
t.Lower();
@@ -1440,7 +1587,7 @@
kMachFloat64};
Node* load = t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0,
- t.p1, t.p1, t.start);
+ t.p1, t.start, t.start);
t.Return(load);
t.Lower();
CHECK_EQ(IrOpcode::kLoad, load->opcode());
@@ -1467,13 +1614,13 @@
TEST(InsertChangeForStoreElement) {
// TODO(titzer): test all load/store representation change insertions.
- TestingGraph t(Type::Any(), Type::Signed32(), Type::Any());
+ TestingGraph t(Type::Any(), Type::Signed32());
ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(),
kMachFloat64};
- Node* store = t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0,
- t.jsgraph.Int32Constant(0), t.p2, t.p1,
- t.start, t.start);
+ Node* store =
+ t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0,
+ t.jsgraph.Int32Constant(0), t.p1, t.start, t.start);
t.Effect(store);
t.Lower();
@@ -1504,10 +1651,11 @@
TestingGraph t(Type::Any(), Type::Signed32());
static const MachineType kMachineTypes[] = {kMachInt32, kMachUint32,
kMachFloat64};
+ Type* kTypes[] = {Type::Signed32(), Type::Unsigned32(), Type::Number()};
for (size_t i = 0; i < arraysize(kMachineTypes); i++) {
FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
- Handle<Name>::null(), Type::Any(), kMachineTypes[i]};
+ Handle<Name>::null(), kTypes[i], kMachineTypes[i]};
Node* load0 =
t.graph()->NewNode(t.simplified()->LoadField(access), t.p0, t.start);
@@ -1525,36 +1673,405 @@
}
-// TODO(titzer): this tests current behavior of assuming an implicit
-// representation change in loading float32s. Fix when float32 is fully
-// supported.
-TEST(ImplicitFloat32ToFloat64InLoads) {
- TestingGraph t(Type::Any());
+TEST(RunNumberDivide_minus_1_TruncatingToInt32) {
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToInt32(t.Parameter(0));
+ Node* div = t.NumberDivide(num, t.jsgraph.Constant(-1));
+ Node* trunc = t.NumberToInt32(div);
+ t.Return(trunc);
- FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
- Handle<Name>::null(), Type::Any(), kMachFloat32};
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
- Node* load =
- t.graph()->NewNode(t.simplified()->LoadField(access), t.p0, t.start);
- t.Return(load);
- t.Lower();
- CHECK_EQ(IrOpcode::kLoad, load->opcode());
- CHECK_EQ(t.p0, load->InputAt(0));
- CheckChangeOf(IrOpcode::kChangeFloat64ToTagged, load, t.ret->InputAt(0));
+ FOR_INT32_INPUTS(i) {
+ int32_t x = 0 - *i;
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
}
-TEST(ImplicitFloat64ToFloat32InStores) {
- TestingGraph t(Type::Any(), Type::Signed32());
- FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
- Handle<Name>::null(), Type::Any(), kMachFloat32};
+TEST(NumberMultiply_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, -1, 0, 1, 100, 1000};
- Node* store = t.graph()->NewNode(t.simplified()->StoreField(access), t.p0,
- t.p1, t.start, t.start);
- t.Effect(store);
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* mul = t.graph()->NewNode(t.simplified()->NumberMultiply(), t.p0, k);
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), mul);
+ t.Return(trunc);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Mul, mul->opcode());
+ }
+}
+
+
+TEST(RunNumberMultiply_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, -1, 0, 1, 100, 1000, 3000999};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ double k = static_cast<double>(constants[i]);
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToInt32(t.Parameter(0));
+ Node* mul = t.NumberMultiply(num, t.jsgraph.Constant(k));
+ Node* trunc = t.NumberToInt32(mul);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_INT32_INPUTS(i) {
+ int32_t x = DoubleToInt32(static_cast<double>(*i) * k);
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(RunNumberMultiply_TruncatingToUint32) {
+ uint32_t constants[] = {0, 1, 2, 3, 4, 100, 1000, 1024, 2048, 3000999};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ double k = static_cast<double>(constants[i]);
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToUint32(t.Parameter(0));
+ Node* mul = t.NumberMultiply(num, t.jsgraph.Constant(k));
+ Node* trunc = t.NumberToUint32(mul);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_UINT32_INPUTS(i) {
+ uint32_t x = DoubleToUint32(static_cast<double>(*i) * k);
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(RunNumberDivide_2_TruncatingToUint32) {
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToUint32(t.Parameter(0));
+ Node* div = t.NumberDivide(num, t.jsgraph.Constant(2));
+ Node* trunc = t.NumberToUint32(div);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_UINT32_INPUTS(i) {
+ uint32_t x = DoubleToUint32(static_cast<double>(*i / 2.0));
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+}
+
+
+TEST(NumberMultiply_ConstantOutOfRange) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(1000000023);
+ Node* mul = t.graph()->NewNode(t.simplified()->NumberMultiply(), t.p0, k);
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), mul);
+ t.Return(trunc);
t.Lower();
- CHECK_EQ(IrOpcode::kStore, store->opcode());
- CHECK_EQ(t.p0, store->InputAt(0));
- CheckChangeOf(IrOpcode::kChangeTaggedToFloat64, t.p1, store->InputAt(2));
+ CHECK_EQ(IrOpcode::kFloat64Mul, mul->opcode());
+}
+
+
+TEST(NumberMultiply_NonTruncating) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(111);
+ Node* mul = t.graph()->NewNode(t.simplified()->NumberMultiply(), t.p0, k);
+ t.Return(mul);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kFloat64Mul, mul->opcode());
+}
+
+
+TEST(NumberDivide_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, 1, 4, 100, 1000};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachInt32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Div, use->InputAt(0)->opcode());
+ }
+}
+
+
+TEST(RunNumberDivide_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, -1, 1, 2, 100, 1000, 1024, 2048};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int32_t k = constants[i];
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToInt32(t.Parameter(0));
+ Node* div = t.NumberDivide(num, t.jsgraph.Constant(k));
+ Node* trunc = t.NumberToInt32(div);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_INT32_INPUTS(i) {
+ if (*i == INT_MAX) continue; // exclude max int.
+ int32_t x = DoubleToInt32(static_cast<double>(*i) / k);
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(NumberDivide_TruncatingToUint32) {
+ double constants[] = {1, 3, 100, 1000, 100998348};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Unsigned32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachUint32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kUint32Div, use->InputAt(0)->opcode());
+ }
+}
+
+
+TEST(RunNumberDivide_TruncatingToUint32) {
+ uint32_t constants[] = {100, 10, 1, 1, 2, 4, 1000, 1024, 2048};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ uint32_t k = constants[i];
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToUint32(t.Parameter(0));
+ Node* div = t.NumberDivide(num, t.jsgraph.Constant(static_cast<double>(k)));
+ Node* trunc = t.NumberToUint32(div);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_UINT32_INPUTS(i) {
+ uint32_t x = *i / k;
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(NumberDivide_BadConstants) {
+ {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(-1);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachInt32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Sub, use->InputAt(0)->opcode());
+ }
+
+ {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(0);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachInt32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Constant, use->InputAt(0)->opcode());
+ CHECK_EQ(0, OpParameter<int32_t>(use->InputAt(0)));
+ }
+
+ {
+ TestingGraph t(Type::Unsigned32());
+ Node* k = t.jsgraph.Constant(0);
+ Node* div = t.graph()->NewNode(t.simplified()->NumberDivide(), t.p0, k);
+ Node* use = t.Use(div, kMachUint32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Constant, use->InputAt(0)->opcode());
+ CHECK_EQ(0, OpParameter<int32_t>(use->InputAt(0)));
+ }
+}
+
+
+TEST(NumberModulus_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, 1, 4, 100, 1000};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* mod = t.graph()->NewNode(t.simplified()->NumberModulus(), t.p0, k);
+ Node* use = t.Use(mod, kMachInt32);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kInt32Mod, use->InputAt(0)->opcode());
+ }
+}
+
+
+TEST(RunNumberModulus_TruncatingToInt32) {
+ int32_t constants[] = {-100, -10, -1, 1, 2, 100, 1000, 1024, 2048};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ int32_t k = constants[i];
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToInt32(t.Parameter(0));
+ Node* mod = t.NumberModulus(num, t.jsgraph.Constant(k));
+ Node* trunc = t.NumberToInt32(mod);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_INT32_INPUTS(i) {
+ if (*i == INT_MAX) continue; // exclude max int.
+ int32_t x = DoubleToInt32(std::fmod(static_cast<double>(*i), k));
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(NumberModulus_TruncatingToUint32) {
+ double constants[] = {1, 3, 100, 1000, 100998348};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Unsigned32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* mod = t.graph()->NewNode(t.simplified()->NumberModulus(), t.p0, k);
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), mod);
+ Node* ret = t.Return(trunc);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kUint32Mod, ret->InputAt(0)->opcode());
+ }
+}
+
+
+TEST(RunNumberModulus_TruncatingToUint32) {
+ uint32_t constants[] = {1, 2, 100, 1000, 1024, 2048};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ uint32_t k = constants[i];
+ SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+ Node* num = t.NumberToUint32(t.Parameter(0));
+ Node* mod =
+ t.NumberModulus(num, t.jsgraph.Constant(static_cast<double>(k)));
+ Node* trunc = t.NumberToUint32(mod);
+ t.Return(trunc);
+
+ if (Pipeline::SupportedTarget()) {
+ t.LowerAllNodesAndLowerChanges();
+ t.GenerateCode();
+
+ FOR_UINT32_INPUTS(i) {
+ uint32_t x = *i % k;
+ t.CheckNumberCall(static_cast<double>(x), static_cast<double>(*i));
+ }
+ }
+ }
+}
+
+
+TEST(NumberModulus_Int32) {
+ int32_t constants[] = {-100, -10, 1, 4, 100, 1000};
+
+ for (size_t i = 0; i < arraysize(constants); i++) {
+ TestingGraph t(Type::Signed32());
+ Node* k = t.jsgraph.Constant(constants[i]);
+ Node* mod = t.graph()->NewNode(t.simplified()->NumberModulus(), t.p0, k);
+ t.Return(mod);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kFloat64Mod, mod->opcode()); // Pesky -0 behavior.
+ }
+}
+
+
+TEST(NumberModulus_Uint32) {
+ const double kConstants[] = {2, 100, 1000, 1024, 2048};
+ const MachineType kTypes[] = {kMachInt32, kMachUint32};
+
+ for (auto const type : kTypes) {
+ for (auto const c : kConstants) {
+ TestingGraph t(Type::Unsigned32());
+ Node* k = t.jsgraph.Constant(c);
+ Node* mod = t.graph()->NewNode(t.simplified()->NumberModulus(), t.p0, k);
+ Node* use = t.Use(mod, type);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(IrOpcode::kUint32Mod, use->InputAt(0)->opcode());
+ }
+ }
+}
+
+
+TEST(PhiRepresentation) {
+ HandleAndZoneScope scope;
+ Zone* z = scope.main_zone();
+
+ struct TestData {
+ Type* arg1;
+ Type* arg2;
+ MachineType use;
+ MachineTypeUnion expected;
+ };
+
+ TestData test_data[] = {
+ {Type::Signed32(), Type::Unsigned32(), kMachInt32,
+ kRepWord32 | kTypeNumber},
+ {Type::Signed32(), Type::Unsigned32(), kMachUint32,
+ kRepWord32 | kTypeNumber},
+ {Type::Signed32(), Type::Signed32(), kMachInt32, kMachInt32},
+ {Type::Unsigned32(), Type::Unsigned32(), kMachInt32, kMachUint32},
+ {Type::Number(), Type::Signed32(), kMachInt32, kMachFloat64},
+ {Type::Signed32(), Type::String(), kMachInt32, kMachAnyTagged}};
+
+ for (auto const d : test_data) {
+ TestingGraph t(d.arg1, d.arg2, Type::Boolean());
+
+ Node* br = t.graph()->NewNode(t.common()->Branch(), t.p2, t.start);
+ Node* tb = t.graph()->NewNode(t.common()->IfTrue(), br);
+ Node* fb = t.graph()->NewNode(t.common()->IfFalse(), br);
+ Node* m = t.graph()->NewNode(t.common()->Merge(2), tb, fb);
+
+ Node* phi =
+ t.graph()->NewNode(t.common()->Phi(kMachAnyTagged, 2), t.p0, t.p1, m);
+
+ Bounds phi_bounds = Bounds::Either(Bounds(d.arg1), Bounds(d.arg2), z);
+ NodeProperties::SetBounds(phi, phi_bounds);
+
+ Node* use = t.Use(phi, d.use);
+ t.Return(use);
+ t.Lower();
+
+ CHECK_EQ(d.expected, OpParameter<MachineType>(phi));
+ }
}
diff --git a/test/cctest/compiler/test-typer.cc b/test/cctest/compiler/test-typer.cc
new file mode 100644
index 0000000..5f7f55a
--- /dev/null
+++ b/test/cctest/compiler/test-typer.cc
@@ -0,0 +1,380 @@
+// 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 <functional>
+
+#include "src/codegen.h"
+#include "src/compiler/js-operator.h"
+#include "src/compiler/node-properties-inl.h"
+#include "src/compiler/typer.h"
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/graph-builder-tester.h"
+#include "test/cctest/types-fuzz.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+
+// TODO(titzer): generate a large set of deterministic inputs for these tests.
+class TyperTester : public HandleAndZoneScope, public GraphAndBuilders {
+ public:
+ TyperTester()
+ : GraphAndBuilders(main_zone()),
+ types_(main_zone(), isolate()),
+ typer_(graph(), MaybeHandle<Context>()),
+ javascript_(main_zone()) {
+ Node* s = graph()->NewNode(common()->Start(3));
+ graph()->SetStart(s);
+ context_node_ = graph()->NewNode(common()->Parameter(2), graph()->start());
+ rng_ = isolate()->random_number_generator();
+
+ integers.push_back(0);
+ integers.push_back(0);
+ integers.push_back(-1);
+ integers.push_back(+1);
+ integers.push_back(-V8_INFINITY);
+ integers.push_back(+V8_INFINITY);
+ for (int i = 0; i < 5; ++i) {
+ double x = rng_->NextInt();
+ integers.push_back(x);
+ x *= rng_->NextInt();
+ if (!IsMinusZero(x)) integers.push_back(x);
+ }
+
+ int32s.push_back(0);
+ int32s.push_back(0);
+ int32s.push_back(-1);
+ int32s.push_back(+1);
+ int32s.push_back(kMinInt);
+ int32s.push_back(kMaxInt);
+ for (int i = 0; i < 10; ++i) {
+ int32s.push_back(rng_->NextInt());
+ }
+ }
+
+ Types<Type, Type*, Zone> types_;
+ Typer typer_;
+ JSOperatorBuilder javascript_;
+ Node* context_node_;
+ v8::base::RandomNumberGenerator* rng_;
+ std::vector<double> integers;
+ std::vector<double> int32s;
+
+ Isolate* isolate() { return main_isolate(); }
+ Graph* graph() { return main_graph_; }
+ CommonOperatorBuilder* common() { return &main_common_; }
+
+ Node* Parameter(int index = 0) {
+ return graph()->NewNode(common()->Parameter(index), graph()->start());
+ }
+
+ Type* TypeBinaryOp(const Operator* op, Type* lhs, Type* rhs) {
+ Node* p0 = Parameter(0);
+ Node* p1 = Parameter(1);
+ NodeProperties::SetBounds(p0, Bounds(lhs));
+ NodeProperties::SetBounds(p1, Bounds(rhs));
+ Node* n = graph()->NewNode(
+ op, p0, p1, context_node_, graph()->start(), graph()->start());
+ return NodeProperties::GetBounds(n).upper;
+ }
+
+ Type* RandomRange(bool int32 = false) {
+ std::vector<double>& numbers = int32 ? int32s : integers;
+ double i = numbers[rng_->NextInt(static_cast<int>(numbers.size()))];
+ double j = numbers[rng_->NextInt(static_cast<int>(numbers.size()))];
+ return NewRange(i, j);
+ }
+
+ Type* NewRange(double i, double j) {
+ Factory* f = isolate()->factory();
+ i::Handle<i::Object> min = f->NewNumber(i);
+ i::Handle<i::Object> max = f->NewNumber(j);
+ if (min->Number() > max->Number()) std::swap(min, max);
+ return Type::Range(min, max, main_zone());
+ }
+
+ double RandomInt(double min, double max) {
+ switch (rng_->NextInt(4)) {
+ case 0: return min;
+ case 1: return max;
+ default: break;
+ }
+ if (min == +V8_INFINITY) return +V8_INFINITY;
+ if (max == -V8_INFINITY) return -V8_INFINITY;
+ if (min == -V8_INFINITY && max == +V8_INFINITY) {
+ return rng_->NextInt() * static_cast<double>(rng_->NextInt());
+ }
+ double result = nearbyint(min + (max - min) * rng_->NextDouble());
+ if (IsMinusZero(result)) return 0;
+ if (std::isnan(result)) return rng_->NextInt(2) ? min : max;
+ DCHECK(min <= result && result <= max);
+ return result;
+ }
+
+ double RandomInt(Type::RangeType* range) {
+ return RandomInt(range->Min()->Number(), range->Max()->Number());
+ }
+
+ // Careful, this function runs O(max_width^5) trials.
+ template <class BinaryFunction>
+ void TestBinaryArithOpCloseToZero(const Operator* op, BinaryFunction opfun,
+ int max_width) {
+ const int min_min = -2 - max_width / 2;
+ const int max_min = 2 + max_width / 2;
+ for (int width = 0; width < max_width; width++) {
+ for (int lmin = min_min; lmin <= max_min; lmin++) {
+ for (int rmin = min_min; rmin <= max_min; rmin++) {
+ Type* r1 = NewRange(lmin, lmin + width);
+ Type* r2 = NewRange(rmin, rmin + width);
+ Type* expected_type = TypeBinaryOp(op, r1, r2);
+
+ for (int x1 = lmin; x1 < lmin + width; x1++) {
+ for (int x2 = rmin; x2 < rmin + width; x2++) {
+ double result_value = opfun(x1, x2);
+ Type* result_type = Type::Constant(
+ isolate()->factory()->NewNumber(result_value), main_zone());
+ CHECK(result_type->Is(expected_type));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ template <class BinaryFunction>
+ void TestBinaryArithOp(const Operator* op, BinaryFunction opfun) {
+ TestBinaryArithOpCloseToZero(op, opfun, 8);
+ for (int i = 0; i < 100; ++i) {
+ Type::RangeType* r1 = RandomRange()->AsRange();
+ Type::RangeType* r2 = RandomRange()->AsRange();
+ Type* expected_type = TypeBinaryOp(op, r1, r2);
+ for (int i = 0; i < 10; i++) {
+ double x1 = RandomInt(r1);
+ double x2 = RandomInt(r2);
+ double result_value = opfun(x1, x2);
+ Type* result_type = Type::Constant(
+ isolate()->factory()->NewNumber(result_value), main_zone());
+ CHECK(result_type->Is(expected_type));
+ }
+ }
+ }
+
+ template <class BinaryFunction>
+ void TestBinaryCompareOp(const Operator* op, BinaryFunction opfun) {
+ for (int i = 0; i < 100; ++i) {
+ Type::RangeType* r1 = RandomRange()->AsRange();
+ Type::RangeType* r2 = RandomRange()->AsRange();
+ Type* expected_type = TypeBinaryOp(op, r1, r2);
+ for (int i = 0; i < 10; i++) {
+ double x1 = RandomInt(r1);
+ double x2 = RandomInt(r2);
+ bool result_value = opfun(x1, x2);
+ Type* result_type =
+ Type::Constant(result_value ? isolate()->factory()->true_value()
+ : isolate()->factory()->false_value(),
+ main_zone());
+ CHECK(result_type->Is(expected_type));
+ }
+ }
+ }
+
+ template <class BinaryFunction>
+ void TestBinaryBitOp(const Operator* op, BinaryFunction opfun) {
+ for (int i = 0; i < 100; ++i) {
+ Type::RangeType* r1 = RandomRange(true)->AsRange();
+ Type::RangeType* r2 = RandomRange(true)->AsRange();
+ Type* expected_type = TypeBinaryOp(op, r1, r2);
+ for (int i = 0; i < 10; i++) {
+ int32_t x1 = static_cast<int32_t>(RandomInt(r1));
+ int32_t x2 = static_cast<int32_t>(RandomInt(r2));
+ double result_value = opfun(x1, x2);
+ Type* result_type = Type::Constant(
+ isolate()->factory()->NewNumber(result_value), main_zone());
+ CHECK(result_type->Is(expected_type));
+ }
+ }
+ }
+
+ Type* RandomSubtype(Type* type) {
+ Type* subtype;
+ do {
+ subtype = types_.Fuzz();
+ } while (!subtype->Is(type));
+ return subtype;
+ }
+
+ void TestBinaryMonotonicity(const Operator* op) {
+ for (int i = 0; i < 50; ++i) {
+ Type* type1 = types_.Fuzz();
+ Type* type2 = types_.Fuzz();
+ Type* type = TypeBinaryOp(op, type1, type2);
+ Type* subtype1 = RandomSubtype(type1);;
+ Type* subtype2 = RandomSubtype(type2);;
+ Type* subtype = TypeBinaryOp(op, subtype1, subtype2);
+ CHECK(subtype->Is(type));
+ }
+ }
+};
+
+
+static int32_t shift_left(int32_t x, int32_t y) { return x << y; }
+static int32_t shift_right(int32_t x, int32_t y) { return x >> y; }
+static int32_t bit_or(int32_t x, int32_t y) { return x | y; }
+static int32_t bit_and(int32_t x, int32_t y) { return x & y; }
+static int32_t bit_xor(int32_t x, int32_t y) { return x ^ y; }
+
+
+//------------------------------------------------------------------------------
+// Soundness
+// For simplicity, we currently only test soundness on expression operators
+// that have a direct equivalent in C++. Also, testing is currently limited
+// to ranges as input types.
+
+
+TEST(TypeJSAdd) {
+ TyperTester t;
+ t.TestBinaryArithOp(t.javascript_.Add(), std::plus<double>());
+}
+
+
+TEST(TypeJSSubtract) {
+ TyperTester t;
+ t.TestBinaryArithOp(t.javascript_.Subtract(), std::minus<double>());
+}
+
+
+TEST(TypeJSMultiply) {
+ TyperTester t;
+ t.TestBinaryArithOp(t.javascript_.Multiply(), std::multiplies<double>());
+}
+
+
+TEST(TypeJSDivide) {
+ TyperTester t;
+ t.TestBinaryArithOp(t.javascript_.Divide(), std::divides<double>());
+}
+
+
+TEST(TypeJSModulus) {
+ TyperTester t;
+ t.TestBinaryArithOp(t.javascript_.Modulus(), modulo);
+}
+
+
+TEST(TypeJSBitwiseOr) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.BitwiseOr(), bit_or);
+}
+
+
+TEST(TypeJSBitwiseAnd) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.BitwiseAnd(), bit_and);
+}
+
+
+TEST(TypeJSBitwiseXor) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.BitwiseXor(), bit_xor);
+}
+
+
+TEST(TypeJSShiftLeft) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.ShiftLeft(), shift_left);
+}
+
+
+TEST(TypeJSShiftRight) {
+ TyperTester t;
+ t.TestBinaryBitOp(t.javascript_.ShiftRight(), shift_right);
+}
+
+
+TEST(TypeJSLessThan) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.LessThan(), std::less<double>());
+}
+
+
+TEST(TypeJSLessThanOrEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(
+ t.javascript_.LessThanOrEqual(), std::less_equal<double>());
+}
+
+
+TEST(TypeJSGreaterThan) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.GreaterThan(), std::greater<double>());
+}
+
+
+TEST(TypeJSGreaterThanOrEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(
+ t.javascript_.GreaterThanOrEqual(), std::greater_equal<double>());
+}
+
+
+TEST(TypeJSEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.Equal(), std::equal_to<double>());
+}
+
+
+TEST(TypeJSNotEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.NotEqual(), std::not_equal_to<double>());
+}
+
+
+// For numbers there's no difference between strict and non-strict equality.
+TEST(TypeJSStrictEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(t.javascript_.StrictEqual(), std::equal_to<double>());
+}
+
+
+TEST(TypeJSStrictNotEqual) {
+ TyperTester t;
+ t.TestBinaryCompareOp(
+ t.javascript_.StrictNotEqual(), std::not_equal_to<double>());
+}
+
+
+//------------------------------------------------------------------------------
+// Monotonicity
+
+
+// List should be in sync with JS_SIMPLE_BINOP_LIST.
+#define JSBINOP_LIST(V) \
+ V(Equal) \
+ V(NotEqual) \
+ V(StrictEqual) \
+ V(StrictNotEqual) \
+ V(LessThan) \
+ V(GreaterThan) \
+ V(LessThanOrEqual) \
+ V(GreaterThanOrEqual) \
+ V(BitwiseOr) \
+ V(BitwiseXor) \
+ V(BitwiseAnd) \
+ V(ShiftLeft) \
+ V(ShiftRight) \
+ V(ShiftRightLogical) \
+ V(Add) \
+ V(Subtract) \
+ V(Multiply) \
+ V(Divide) \
+ V(Modulus)
+
+
+#define TEST_FUNC(name) \
+ TEST(Monotonicity_##name) { \
+ TyperTester t; \
+ t.TestBinaryMonotonicity(t.javascript_.name()); \
+ }
+JSBINOP_LIST(TEST_FUNC)
+#undef TEST_FUNC
diff --git a/test/cctest/compiler/value-helper.h b/test/cctest/compiler/value-helper.h
index b5da982..218a773 100644
--- a/test/cctest/compiler/value-helper.h
+++ b/test/cctest/compiler/value-helper.h
@@ -60,6 +60,45 @@
CheckHeapConstant(isolate_->heap()->false_value(), node);
}
+ static std::vector<float> float32_vector() {
+ static const float kValues[] = {
+ -std::numeric_limits<float>::infinity(), -2.70497e+38f, -1.4698e+37f,
+ -1.22813e+35f, -1.20555e+35f, -1.34584e+34f,
+ -1.0079e+32f, -6.49364e+26f, -3.06077e+25f,
+ -1.46821e+25f, -1.17658e+23f, -1.9617e+22f,
+ -2.7357e+20f, -1.48708e+13f, -1.89633e+12f,
+ -4.66622e+11f, -2.22581e+11f, -1.45381e+10f,
+ -1.3956e+09f, -1.32951e+09f, -1.30721e+09f,
+ -1.19756e+09f, -9.26822e+08f, -6.35647e+08f,
+ -4.00037e+08f, -1.81227e+08f, -5.09256e+07f,
+ -964300.0f, -192446.0f, -28455.0f,
+ -27194.0f, -26401.0f, -20575.0f,
+ -17069.0f, -9167.0f, -960.178f,
+ -113.0f, -62.0f, -15.0f,
+ -7.0f, -0.0256635f, -4.60374e-07f,
+ -3.63759e-10f, -4.30175e-14f, -5.27385e-15f,
+ -1.48084e-15f, -1.05755e-19f, -3.2995e-21f,
+ -1.67354e-23f, -1.11885e-23f, -1.78506e-30f,
+ -5.07594e-31f, -3.65799e-31f, -1.43718e-34f,
+ -1.27126e-38f, -0.0f, 0.0f,
+ 1.17549e-38f, 1.56657e-37f, 4.08512e-29f,
+ 3.31357e-28f, 6.25073e-22f, 4.1723e-13f,
+ 1.44343e-09f, 5.27004e-08f, 9.48298e-08f,
+ 5.57888e-07f, 4.89988e-05f, 0.244326f,
+ 12.4895f, 19.0f, 47.0f,
+ 106.0f, 538.324f, 564.536f,
+ 819.124f, 7048.0f, 12611.0f,
+ 19878.0f, 20309.0f, 797056.0f,
+ 1.77219e+09f, 1.51116e+11f, 4.18193e+13f,
+ 3.59167e+16f, 3.38211e+19f, 2.67488e+20f,
+ 1.78831e+21f, 9.20914e+21f, 8.35654e+23f,
+ 1.4495e+24f, 5.94015e+25f, 4.43608e+30f,
+ 2.44502e+33f, 2.61152e+33f, 1.38178e+37f,
+ 1.71306e+37f, 3.31899e+38f, 3.40282e+38f,
+ std::numeric_limits<float>::infinity()};
+ return std::vector<float>(&kValues[0], &kValues[arraysize(kValues)]);
+ }
+
static std::vector<double> float64_vector() {
static const double nan = v8::base::OS::nan_value();
static const double values[] = {
@@ -82,6 +121,8 @@
static const std::vector<uint32_t> uint32_vector() {
static const uint32_t kValues[] = {
0x00000000, 0x00000001, 0xffffffff, 0x1b09788b, 0x04c5fce8, 0xcc0de5bf,
+ // This row is useful for testing lea optimizations on intel.
+ 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00000008, 0x00000009,
0x273a798e, 0x187937a3, 0xece3af83, 0x5495a16b, 0x0b668ecc, 0x11223344,
0x0000009e, 0x00000043, 0x0000af73, 0x0000116b, 0x00658ecc, 0x002b3b4c,
0x88776655, 0x70000000, 0x07200000, 0x7fffffff, 0x56123761, 0x7fffff00,
@@ -117,6 +158,7 @@
#define FOR_INT32_INPUTS(var) FOR_INPUTS(int32_t, int32, var)
#define FOR_UINT32_INPUTS(var) FOR_INPUTS(uint32_t, uint32, var)
+#define FOR_FLOAT32_INPUTS(var) FOR_INPUTS(float, float32, var)
#define FOR_FLOAT64_INPUTS(var) FOR_INPUTS(double, float64, var)
#define FOR_INT32_SHIFTS(var) for (int32_t var = 0; var < 32; var++)