globals_(0, local_zone),
execution_control_(nullptr),
execution_context_(nullptr),
- function_context_(nullptr),
input_buffer_size_(0),
input_buffer_(nullptr),
exit_control_(nullptr),
}
-Node* AstGraphBuilder::GetFunctionContext() {
- DCHECK(function_context_ != nullptr);
- return function_context_;
+void AstGraphBuilder::CreateFunctionContext(bool constant_context) {
+ function_context_.set(constant_context
+ ? jsgraph()->HeapConstant(info()->context())
+ : NewOuterContextParam());
}
}
-bool AstGraphBuilder::CreateGraph() {
+bool AstGraphBuilder::CreateGraph(bool constant_context) {
Scope* scope = info()->scope();
DCHECK(graph() != NULL);
}
// Initialize the incoming context.
- function_context_ = NewOuterContextParam();
- ContextScope incoming(this, scope, function_context_);
+ CreateFunctionContext(constant_context);
+ ContextScope incoming(this, scope, function_context_.get());
// Build receiver check for sloppy mode if necessary.
// TODO(mstarzinger/verwaest): Should this be moved back into the CallIC?
if (heap_slots > 0) {
// Push a new inner context scope for the function.
Node* closure = GetFunctionClosure();
- Node* inner_context = BuildLocalFunctionContext(function_context_, closure);
+ Node* inner_context =
+ BuildLocalFunctionContext(function_context_.get(), closure);
ContextScope top_context(this, scope, inner_context);
CreateGraphBody();
} else {
Node* AstGraphBuilder::BuildLoadGlobalObject() {
- Node* context = GetFunctionContext();
const Operator* load_op =
javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true);
- return NewNode(load_op, context);
+ return NewNode(load_op, function_context_.get());
}
void AstGraphBuilder::Environment::Merge(Environment* other) {
DCHECK(values_.size() == other->values_.size());
+ // TODO(titzer): make context stack heights match.
DCHECK(contexts_.size() <= other->contexts_.size());
// Nothing to do if the other environment is dead.
graph()->NewNode(common()->Merge(1), arraysize(inputs), inputs, true);
effect_dependency_ = other->effect_dependency_;
values_ = other->values_;
+ // TODO(titzer): make context stack heights match.
+ size_t min = std::min(contexts_.size(), other->contexts_.size());
+ contexts_ = other->contexts_;
+ contexts_.resize(min, nullptr);
return;
}
LoopAssignmentAnalysis* loop_assignment = NULL);
// Creates a graph by visiting the entire AST.
- bool CreateGraph();
+ bool CreateGraph(bool constant_context);
// Helpers to create new control nodes.
Node* NewIfTrue() { return NewNode(common()->IfTrue()); }
// Visiting function for declarations list is overridden.
void VisitDeclarations(ZoneList<Declaration*>* declarations) OVERRIDE;
- // Get the node that represents the outer function context.
- Node* GetFunctionContext();
-
- // Get the node that represents the outer function closure.
- Node* GetFunctionClosure();
-
private:
class AstContext;
class AstEffectContext;
// Nodes representing values in the activation record.
SetOncePointer<Node> function_closure_;
- Node* function_context_;
+ SetOncePointer<Node> function_context_;
// Temporary storage for building node input lists.
int input_buffer_size_;
void set_execution_context(ContextScope* ctx) { execution_context_ = ctx; }
void set_exit_control(Node* exit) { exit_control_ = exit; }
+ // Create the main graph body by visiting the AST.
void CreateGraphBody();
+ // Create the node that represents the outer context of the function.
+ void CreateFunctionContext(bool constant_context);
+
+ // Get or create the node that represents the outer function closure.
+ Node* GetFunctionClosure();
+
// Node creation helpers.
Node* NewNode(const Operator* op, bool incomplete = false) {
return MakeNode(op, 0, static_cast<Node**>(NULL), incomplete);
}
#if DEBUG
// Verify that no inputs to live nodes are NULL.
- for (size_t j = 0; j < nodes.size(); j++) {
- Node* node = nodes[j];
- for (Node* const input : node->inputs()) {
- CHECK(input);
+ for (Node* node : nodes) {
+ for (int index = 0; index < node->InputCount(); index++) {
+ Node* input = node->InputAt(index);
+ if (input == nullptr) {
+ std::ostringstream str;
+ str << "GraphError: node #" << node->id() << ":" << *node->op()
+ << "(input @" << index << ") == null";
+ FATAL(str.str().c_str());
+ }
+ if (input->opcode() == IrOpcode::kDead) {
+ std::ostringstream str;
+ str << "GraphError: node #" << node->id() << ":" << *node->op()
+ << "(input @" << index << ") == dead";
+ FATAL(str.str().c_str());
+ }
}
- for (Node* const use : node->uses()) {
+ for (Node* use : node->uses()) {
CHECK(marked.IsReachableFromEnd(use));
}
}
namespace compiler {
Reduction JSContextSpecializer::Reduce(Node* node) {
- if (node == context_) {
- Node* constant = jsgraph_->Constant(ctx_);
- NodeProperties::ReplaceWithValue(node, constant);
- return Replace(constant);
- }
if (node->opcode() == IrOpcode::kJSLoadContext) {
return ReduceJSLoadContext(node);
}
// some {LoadContext} nodes or strength reducing some {StoreContext} nodes.
class JSContextSpecializer : public Reducer {
public:
- JSContextSpecializer(Handle<Context> ctx, JSGraph* jsgraph, Node* context)
- : ctx_(ctx), jsgraph_(jsgraph), context_(context) {}
+ explicit JSContextSpecializer(JSGraph* jsgraph) : jsgraph_(jsgraph) {}
Reduction Reduce(Node* node) OVERRIDE;
Reduction ReduceJSStoreContext(Node* node);
private:
- Handle<Context> ctx_;
JSGraph* jsgraph_;
- Node* context_;
};
}
}
jsgraph_->javascript(), jsgraph_->machine());
AstGraphBuilder graph_builder(local_zone_, &info, &jsgraph);
- graph_builder.CreateGraph();
+ graph_builder.CreateGraph(false);
Inlinee::UnifyReturn(&jsgraph);
CopyVisitor visitor(&graph, jsgraph_->graph(), info.zone());
if (backedges == 1) {
// Simple case. Map the incoming edges to the loop to the previous copy.
for (Node* node : loop_tree->HeaderNodes(loop)) {
+ if (!all.IsLive(node)) continue; // dead phi hanging off loop.
Node* copy = mapping->at(node->id());
Node* backedge = node->InputAt(1);
if (previous) backedge = previous->at(backedge->id());
Node* merge =
graph->NewNode(common->Merge(backedges), backedges, &tmp_inputs[0]);
for (Node* node : loop_tree->HeaderNodes(loop)) {
+ if (!all.IsLive(node)) continue; // dead phi hanging off loop.
Node* copy = mapping->at(node->id());
if (node == loop_header) {
// The entry to the loop is the merge.
// Replace the normal entry with {Dead} and the loop entry with {Start}
// and run the control reducer to clean up the graph.
osr_normal_entry->ReplaceUses(dead);
+ osr_normal_entry->Kill();
osr_loop_entry->ReplaceUses(graph->start());
+ osr_loop_entry->Kill();
// Normally the control reducer removes loops whose first input is dead,
// but we need to avoid that because the osr_loop is reachable through
javascript_(nullptr),
jsgraph_(nullptr),
typer_(nullptr),
- context_node_(nullptr),
schedule_(nullptr),
instruction_zone_scope_(zone_pool_),
instruction_zone_(instruction_zone_scope_.zone()),
javascript_(nullptr),
jsgraph_(nullptr),
typer_(nullptr),
- context_node_(nullptr),
schedule_(schedule),
instruction_zone_scope_(zone_pool_),
instruction_zone_(instruction_zone_scope_.zone()),
javascript_(nullptr),
jsgraph_(nullptr),
typer_(nullptr),
- context_node_(nullptr),
schedule_(nullptr),
instruction_zone_scope_(zone_pool_),
instruction_zone_(sequence->zone()),
loop_assignment_ = loop_assignment;
}
- Node* context_node() const { return context_node_; }
- void set_context_node(Node* context_node) {
- DCHECK(!context_node_);
- context_node_ = context_node;
- }
-
Schedule* schedule() const { return schedule_; }
void set_schedule(Schedule* schedule) {
DCHECK(!schedule_);
common_ = nullptr;
javascript_ = nullptr;
jsgraph_ = nullptr;
- context_node_ = nullptr;
schedule_ = nullptr;
}
JSGraph* jsgraph_;
// TODO(dcarney): make this into a ZoneObject.
SmartPointer<Typer> typer_;
- Node* context_node_;
Schedule* schedule_;
// All objects in the following group of fields are allocated in
source_positions_(source_positions),
start_position_(info->shared_info()->start_position()) {}
- bool CreateGraph() {
+ bool CreateGraph(bool constant_context) {
SourcePositionTable::Scope pos_scope(source_positions_, start_position_);
- return AstGraphBuilder::CreateGraph();
+ return AstGraphBuilder::CreateGraph(constant_context);
}
#define DEF_VISIT(type) \
AST_NODE_LIST(DEF_VISIT)
#undef DEF_VISIT
- Node* GetFunctionContext() { return AstGraphBuilder::GetFunctionContext(); }
-
private:
SourcePositionTable* source_positions_;
SourcePosition start_position_;
struct GraphBuilderPhase {
static const char* phase_name() { return "graph builder"; }
- void Run(PipelineData* data, Zone* temp_zone) {
+ void Run(PipelineData* data, Zone* temp_zone, bool constant_context) {
AstGraphBuilderWithPositions graph_builder(
temp_zone, data->info(), data->jsgraph(), data->loop_assignment(),
data->source_positions());
- if (graph_builder.CreateGraph()) {
- data->set_context_node(graph_builder.GetFunctionContext());
- } else {
+ if (!graph_builder.CreateGraph(constant_context)) {
data->set_compilation_failed();
}
}
void Run(PipelineData* data, Zone* temp_zone) {
SourcePositionTable::Scope pos(data->source_positions(),
SourcePosition::Unknown());
- JSContextSpecializer spec(data->info()->context(), data->jsgraph(),
- data->context_node());
+ JSContextSpecializer spec(data->jsgraph());
GraphReducer graph_reducer(data->graph(), temp_zone);
AddReducer(data, &graph_reducer, &spec);
graph_reducer.ReduceGraph();
Run<LoopAssignmentAnalysisPhase>();
}
- Run<GraphBuilderPhase>();
+ Run<GraphBuilderPhase>(info()->is_context_specializing());
if (data.compilation_failed()) return Handle<Code>::null();
RunPrintAndVerify("Initial untyped", true);
void CheckNotTyped(Node* node) {
if (NodeProperties::IsTyped(node)) {
std::ostringstream str;
- str << "TypeError: node #" << node->opcode() << ":"
- << node->op()->mnemonic() << " should never have a type";
- V8_Fatal(__FILE__, __LINE__, str.str().c_str());
+ str << "TypeError: node #" << node->id() << ":" << *node->op()
+ << " should never have a type";
+ FATAL(str.str().c_str());
}
}
void CheckUpperIs(Node* node, Type* type) {
if (typing == TYPED && !bounds(node).upper->Is(type)) {
std::ostringstream str;
- str << "TypeError: node #" << node->opcode() << ":"
- << node->op()->mnemonic() << " upper bound ";
+ str << "TypeError: node #" << node->id() << ":" << *node->op()
+ << " upper bound ";
bounds(node).upper->PrintTo(str);
str << " is not ";
type->PrintTo(str);
- V8_Fatal(__FILE__, __LINE__, str.str().c_str());
+ FATAL(str.str().c_str());
}
}
void CheckUpperMaybe(Node* node, Type* type) {
if (typing == TYPED && !bounds(node).upper->Maybe(type)) {
std::ostringstream str;
- str << "TypeError: node #" << node->opcode() << ":"
- << node->op()->mnemonic() << " upper bound ";
+ str << "TypeError: node #" << node->id() << ":" << *node->op()
+ << " upper bound ";
bounds(node).upper->PrintTo(str);
str << " must intersect ";
type->PrintTo(str);
- V8_Fatal(__FILE__, __LINE__, str.str().c_str());
+ FATAL(str.str().c_str());
}
}
void CheckValueInputIs(Node* node, int i, Type* type) {
Node* input = ValueInput(node, i);
if (typing == TYPED && !bounds(input).upper->Is(type)) {
std::ostringstream str;
- str << "TypeError: node #" << node->opcode() << ":"
- << node->op()->mnemonic() << "(input @" << i << " = "
- << input->opcode() << ":" << input->op()->mnemonic()
- << ") upper bound ";
+ str << "TypeError: node #" << node->id() << ":" << *node->op()
+ << "(input @" << i << " = " << input->opcode() << ":"
+ << input->op()->mnemonic() << ") upper bound ";
bounds(input).upper->PrintTo(str);
str << " is not ";
type->PrintTo(str);
- V8_Fatal(__FILE__, __LINE__, str.str().c_str());
+ FATAL(str.str().c_str());
}
}
};
ReducePhiIterative(expect, phi); // iterative should give the same result.
}
+ // Checks one-step reduction of a phi.
+ void ReducePhiNonIterative(Node* expect, Node* phi) {
+ Node* result = ControlReducer::ReducePhiForTesting(&jsgraph, &common, phi);
+ CHECK_EQ(expect, 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);
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);
+ R.ReducePhiNonIterative(phi1, phi1);
Node* phi2 = R.Phi(a, b, R.dead);
- R.ReducePhi(phi2, phi2);
+ R.ReducePhiNonIterative(phi2, phi2);
}
}
Node* const_context = t.jsgraph()->Constant(native);
Node* deep_const_context = t.jsgraph()->Constant(subcontext2);
Node* param_context = t.NewNode(t.common()->Parameter(0), start);
- JSContextSpecializer spec(Handle<Context>(), t.jsgraph(), const_context);
+ JSContextSpecializer spec(t.jsgraph());
{
// Mutable slot, constant context, depth = 0 => do nothing.
Node* const_context = t.jsgraph()->Constant(native);
Node* deep_const_context = t.jsgraph()->Constant(subcontext2);
Node* param_context = t.NewNode(t.common()->Parameter(0), start);
- JSContextSpecializer spec(Handle<Context>(), t.jsgraph(), const_context);
+ JSContextSpecializer spec(t.jsgraph());
{
// Mutable slot, constant context, depth = 0 => do nothing.
Node* const_context = t.jsgraph()->Constant(native);
Node* param_context = t.NewNode(t.common()->Parameter(0), start);
- JSContextSpecializer spec(native, t.jsgraph(), const_context);
+ JSContextSpecializer spec(t.jsgraph());
{
// Check that specialization replaces values and forwards effects
assertEquals(4950, foo()());
assertEquals(4950, foo()());
assertEquals(4950, foo()());
-
-function bar() {
- var result;
- {
- let sum = 0;
- for (let i = 0; i < 90; i++) {
- sum += i;
- if (i == 45) %OptimizeOsr();
- }
- result = ret;
- function ret() {
- return sum;
- }
- }
- return result;
-}
-
-assertEquals(4005, bar()());
-assertEquals(4005, bar()());
-assertEquals(4005, bar()());
"use strict";
+function nest(body, name, depth) {
+ var header = "";
+ for (var i = 0; i < depth; i++) {
+ var x = "x" + (i + 1);
+ header += " for(var " + x + " = 0; " + x + " < 2; " + x + " = " + x + " + 1 | 0) {\n";
+ body = body + "}"
+ }
+
+ return body.replace(new RegExp("function " + name + "\\(\\) {"),
+ "function " + name + "_" + x + "() {\n" + header);
+}
+
+function test(expected, func, depth) {
+ assertEquals(expected, func());
+ assertEquals(expected, func());
+ assertEquals(expected, func());
+
+ var orig = func.toString();
+ var name = func.name;
+ for (var depth = 1; depth < 4; depth++) {
+ var body = nest(orig, name, depth);
+ func = eval("(" + body + ")");
+
+ assertEquals(expected, func());
+ assertEquals(expected, func());
+ assertEquals(expected, func());
+ }
+}
+
function foo() {
var result;
{
let sum = 0;
- for (var i = 0; i < 100; i++) {
- if (i == 50) %OptimizeOsr();
+ for (var i = 0; i < 10; i++) {
+ %OptimizeOsr();
sum += i;
}
result = sum;
return result;
}
-assertEquals(4950, foo());
-assertEquals(4950, foo());
-assertEquals(4950, foo());
+test(45, foo);
function bar() {
- var result;
+ let sum = 0;
+ for (var i = 0; i < 10; i++) {
+ %OptimizeOsr();
+ sum += i;
+ }
+ return sum;
+}
+
+test(45, bar);
+
+function bon() {
{
let sum = 0;
- for (let i = 0; i < 90; i++) {
+ for (var i = 0; i < 10; i++) {
+ if (i == 5) %OptimizeOsr();
sum += i;
- if (i == 45) %OptimizeOsr();
}
- result = sum;
+ return sum;
+ }
+}
+
+test(45, bon);
+
+function row() {
+ var i = 0;
+ {
+ let sum = 0;
+ while (true) {
+ if (i == 8) return sum;
+ %OptimizeOsr();
+ sum = i;
+ i = i + 1 | 0;
+ }
+ }
+ return 11;
+}
+
+test(7, row);
+
+function nub() {
+ let i = 0;
+ while (i < 2) {
+ %OptimizeOsr();
+ i++;
+ }
+ return i;
+}
+
+test(2, nub);
+
+function kub() {
+ var result = 0;
+ let i = 0;
+ while (i < 2) {
+ let x = i;
+ %OptimizeOsr();
+ i++;
+ result = x;
}
return result;
}
-assertEquals(4005, bar());
-assertEquals(4005, bar());
-assertEquals(4005, bar());
+test(1, kub);
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax --use-osr --turbo-osr
+
+"use strict";
+
+function test(expected, func) {
+ assertEquals(expected, func());
+ assertEquals(expected, func());
+ assertEquals(expected, func());
+}
+
+function bar() {
+ var result;
+ {
+ let sum = 0;
+ for (let i = 0; i < 90; i++) {
+ sum += i;
+ if (i == 45) %OptimizeOsr();
+ }
+ result = sum;
+ }
+ return result;
+}
+
+test(4005, bar);
+
+function baz() {
+ let sum = 0;
+ for (let i = 0; i < 2; i++) {
+ sum = 2;
+ %OptimizeOsr();
+ }
+ return sum;
+}
+
+test(2, baz);
+
+function qux() {
+ var result = 0;
+ for (let i = 0; i < 2; i++) {
+ result = i;
+ %OptimizeOsr();
+ }
+ return result;
+}
+
+test(1, qux);
+
+function nux() {
+ var result = 0;
+ for (let i = 0; i < 2; i++) {
+ {
+ let sum = i;
+ %OptimizeOsr();
+ result = sum;
+ }
+ }
+ return result;
+}
+
+test(1, nux);
+
+function blo() {
+ var result;
+ {
+ let sum = 0;
+ for (let i = 0; i < 90; i++) {
+ sum += i;
+ if (i == 45) %OptimizeOsr();
+ }
+ result = ret;
+ function ret() {
+ return sum;
+ }
+ }
+ return result;
+}
+
+test(4005, blo());
}
}
}
- print(count);
return sum;
}
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax --use-osr --turbo-osr
+
+"use strict";
+
+function test(expected, func) {
+ assertEquals(expected, func());
+ assertEquals(expected, func());
+ assertEquals(expected, func());
+}
+
+function foo() {
+ var result = 0;
+ {
+ let x = 0;
+ var temp_x = x;
+ var first = 1;
+ outer: while (true) {
+ let x = temp_x;
+ if (first == 1) first = 0;
+ else x = x + 1 | 0;
+ var flag = 1;
+ for (; flag == 1; (flag = 0, temp_x = x)) {
+ if (x < 2) {
+ result = x; %OptimizeOsr();
+ } else {
+ break outer;
+ }
+ }
+ if (flag == 1) break;
+ }
+ }
+ return result;
+}
+
+test(1, foo);
+
+
+function smo() {
+ var result = 0;
+ {
+ let x = 11;
+ outer: while (true) {
+ let y = x;
+ for (var i = 0; i < 5; i++) {
+ %OptimizeOsr();
+ if (i) break outer;
+ else result = y;
+ }
+ }
+ }
+ return result;
+}
+
+test(11, smo);