[turbofan] Simplify context specialization and fix for OSR.
AstGraphBuilder puts a constant context in from the beginning. Also fix bug in merging contexts in environment. R=mstarzinger@chromium.org BUG= Review URL: https://codereview.chromium.org/934293002 Cr-Commit-Position: refs/heads/master@{#26745}
This commit is contained in:
parent
4c082b570d
commit
d050c331eb
@ -379,7 +379,6 @@ AstGraphBuilder::AstGraphBuilder(Zone* local_zone, CompilationInfo* info,
|
||||
globals_(0, local_zone),
|
||||
execution_control_(nullptr),
|
||||
execution_context_(nullptr),
|
||||
function_context_(nullptr),
|
||||
input_buffer_size_(0),
|
||||
input_buffer_(nullptr),
|
||||
exit_control_(nullptr),
|
||||
@ -399,9 +398,10 @@ Node* AstGraphBuilder::GetFunctionClosure() {
|
||||
}
|
||||
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
@ -420,7 +420,7 @@ Node* AstGraphBuilder::NewCurrentContextOsrValue() {
|
||||
}
|
||||
|
||||
|
||||
bool AstGraphBuilder::CreateGraph() {
|
||||
bool AstGraphBuilder::CreateGraph(bool constant_context) {
|
||||
Scope* scope = info()->scope();
|
||||
DCHECK(graph() != NULL);
|
||||
|
||||
@ -442,8 +442,8 @@ bool AstGraphBuilder::CreateGraph() {
|
||||
}
|
||||
|
||||
// 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?
|
||||
@ -456,7 +456,8 @@ bool AstGraphBuilder::CreateGraph() {
|
||||
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 {
|
||||
@ -2760,10 +2761,9 @@ Node* AstGraphBuilder::BuildLoadBuiltinsObject() {
|
||||
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
@ -3023,6 +3023,7 @@ void AstGraphBuilder::UpdateControlDependencyToLeaveFunction(Node* exit) {
|
||||
|
||||
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.
|
||||
@ -3037,6 +3038,10 @@ void AstGraphBuilder::Environment::Merge(Environment* other) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ class AstGraphBuilder : public AstVisitor {
|
||||
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()); }
|
||||
@ -51,12 +51,6 @@ class AstGraphBuilder : public AstVisitor {
|
||||
// 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;
|
||||
@ -88,7 +82,7 @@ class AstGraphBuilder : public AstVisitor {
|
||||
|
||||
// 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_;
|
||||
@ -127,8 +121,15 @@ class AstGraphBuilder : public AstVisitor {
|
||||
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);
|
||||
|
@ -291,12 +291,23 @@ class ControlReducerImpl {
|
||||
}
|
||||
#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));
|
||||
}
|
||||
}
|
||||
|
@ -14,11 +14,6 @@ namespace internal {
|
||||
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);
|
||||
}
|
||||
|
@ -17,8 +17,7 @@ namespace compiler {
|
||||
// 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;
|
||||
|
||||
@ -27,9 +26,7 @@ class JSContextSpecializer : public Reducer {
|
||||
Reduction ReduceJSStoreContext(Node* node);
|
||||
|
||||
private:
|
||||
Handle<Context> ctx_;
|
||||
JSGraph* jsgraph_;
|
||||
Node* context_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -364,7 +364,7 @@ Reduction JSInliner::TryInlineJSCall(Node* call_node,
|
||||
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());
|
||||
|
@ -103,6 +103,7 @@ static void PeelOuterLoopsForOsr(Graph* graph, CommonOperatorBuilder* common,
|
||||
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());
|
||||
@ -119,6 +120,7 @@ static void PeelOuterLoopsForOsr(Graph* graph, CommonOperatorBuilder* common,
|
||||
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.
|
||||
@ -214,7 +216,9 @@ bool OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common,
|
||||
// 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
|
||||
|
@ -74,7 +74,6 @@ class PipelineData {
|
||||
javascript_(nullptr),
|
||||
jsgraph_(nullptr),
|
||||
typer_(nullptr),
|
||||
context_node_(nullptr),
|
||||
schedule_(nullptr),
|
||||
instruction_zone_scope_(zone_pool_),
|
||||
instruction_zone_(instruction_zone_scope_.zone()),
|
||||
@ -114,7 +113,6 @@ class PipelineData {
|
||||
javascript_(nullptr),
|
||||
jsgraph_(nullptr),
|
||||
typer_(nullptr),
|
||||
context_node_(nullptr),
|
||||
schedule_(schedule),
|
||||
instruction_zone_scope_(zone_pool_),
|
||||
instruction_zone_(instruction_zone_scope_.zone()),
|
||||
@ -141,7 +139,6 @@ class PipelineData {
|
||||
javascript_(nullptr),
|
||||
jsgraph_(nullptr),
|
||||
typer_(nullptr),
|
||||
context_node_(nullptr),
|
||||
schedule_(nullptr),
|
||||
instruction_zone_scope_(zone_pool_),
|
||||
instruction_zone_(sequence->zone()),
|
||||
@ -186,12 +183,6 @@ class PipelineData {
|
||||
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_);
|
||||
@ -217,7 +208,6 @@ class PipelineData {
|
||||
common_ = nullptr;
|
||||
javascript_ = nullptr;
|
||||
jsgraph_ = nullptr;
|
||||
context_node_ = nullptr;
|
||||
schedule_ = nullptr;
|
||||
}
|
||||
|
||||
@ -272,7 +262,6 @@ class PipelineData {
|
||||
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
|
||||
@ -330,9 +319,9 @@ class AstGraphBuilderWithPositions : public AstGraphBuilder {
|
||||
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) \
|
||||
@ -344,8 +333,6 @@ class AstGraphBuilderWithPositions : public AstGraphBuilder {
|
||||
AST_NODE_LIST(DEF_VISIT)
|
||||
#undef DEF_VISIT
|
||||
|
||||
Node* GetFunctionContext() { return AstGraphBuilder::GetFunctionContext(); }
|
||||
|
||||
private:
|
||||
SourcePositionTable* source_positions_;
|
||||
SourcePosition start_position_;
|
||||
@ -433,13 +420,11 @@ struct LoopAssignmentAnalysisPhase {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -452,8 +437,7 @@ struct ContextSpecializerPhase {
|
||||
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();
|
||||
@ -918,7 +902,7 @@ Handle<Code> Pipeline::GenerateCode() {
|
||||
Run<LoopAssignmentAnalysisPhase>();
|
||||
}
|
||||
|
||||
Run<GraphBuilderPhase>();
|
||||
Run<GraphBuilderPhase>(info()->is_context_specializing());
|
||||
if (data.compilation_failed()) return Handle<Code>::null();
|
||||
RunPrintAndVerify("Initial untyped", true);
|
||||
|
||||
|
@ -68,45 +68,44 @@ class Verifier::Visitor {
|
||||
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());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -153,6 +153,12 @@ class ControlReducerTester : HandleAndZoneScope {
|
||||
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);
|
||||
@ -484,10 +490,10 @@ TEST(CReducePhi2_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);
|
||||
R.ReducePhiNonIterative(phi1, phi1);
|
||||
|
||||
Node* phi2 = R.Phi(a, b, R.dead);
|
||||
R.ReducePhi(phi2, phi2);
|
||||
R.ReducePhiNonIterative(phi2, phi2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ TEST(ReduceJSLoadContext) {
|
||||
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.
|
||||
@ -132,7 +132,7 @@ TEST(ReduceJSStoreContext) {
|
||||
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.
|
||||
@ -197,7 +197,7 @@ TEST(SpecializeToContext) {
|
||||
|
||||
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
|
||||
|
@ -25,23 +25,3 @@ function foo() {
|
||||
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()());
|
||||
|
@ -6,12 +6,41 @@
|
||||
|
||||
"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;
|
||||
@ -19,23 +48,69 @@ function foo() {
|
||||
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);
|
||||
|
82
test/mjsunit/compiler/osr-for-let.js
Normal file
82
test/mjsunit/compiler/osr-for-let.js
Normal file
@ -0,0 +1,82 @@
|
||||
// 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());
|
@ -42,7 +42,6 @@ function bar(goal) {
|
||||
}
|
||||
}
|
||||
}
|
||||
print(count);
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
58
test/mjsunit/compiler/osr-while-let.js
Normal file
58
test/mjsunit/compiler/osr-while-let.js
Normal file
@ -0,0 +1,58 @@
|
||||
// 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);
|
Loading…
Reference in New Issue
Block a user