From: kmillikin@chromium.org Date: Thu, 9 Dec 2010 12:49:53 +0000 (+0000) Subject: Change the HGraphBuilder to dispatch on the context. X-Git-Tag: upstream/4.7.83~20851 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a695d0731e8173f1a679fabadb4a7178b249272a;p=platform%2Fupstream%2Fv8.git Change the HGraphBuilder to dispatch on the context. Before, expressions didn't take advantage of knowing their context in the AST. Now, we use the context to decide what to do with a value at the end of visiting an expression. Review URL: http://codereview.chromium.org/5620007 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5954 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/hydrogen.cc b/src/hydrogen.cc index ad327c4..153bd9a 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -1983,6 +1983,9 @@ void HGraph::InsertRepresentationChanges() { AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind) : owner_(owner), kind_(kind), outer_(owner->ast_context()) { owner->set_ast_context(this); // Push. +#ifdef DEBUG + original_count_ = owner->environment()->total_count(); +#endif } @@ -1991,6 +1994,101 @@ AstContext::~AstContext() { } +EffectContext::~EffectContext() { + ASSERT(owner()->HasStackOverflow() || + !owner()->subgraph()->HasExit() || + owner()->environment()->total_count() == original_count_); +} + + +ValueContext::~ValueContext() { + ASSERT(owner()->HasStackOverflow() || + !owner()->subgraph()->HasExit() || + owner()->environment()->total_count() == original_count_ + 1); +} + + +void EffectContext::ReturnValue(HValue* value) { + // The value is simply ignored. +} + + +void ValueContext::ReturnValue(HValue* value) { + // The value is tracked in the bailout environment, and communicated + // through the environment as the result of the expression. + owner()->Push(value); +} + + +void TestContext::ReturnValue(HValue* value) { + BuildBranch(value); +} + + +void EffectContext::ReturnInstruction(HInstruction* instr, int ast_id) { + owner()->AddInstruction(instr); + if (instr->HasSideEffects()) owner()->AddSimulate(ast_id); +} + + +void ValueContext::ReturnInstruction(HInstruction* instr, int ast_id) { + owner()->AddInstruction(instr); + owner()->Push(instr); + if (instr->HasSideEffects()) owner()->AddSimulate(ast_id); +} + + +void TestContext::ReturnInstruction(HInstruction* instr, int ast_id) { + HGraphBuilder* builder = owner(); + builder->AddInstruction(instr); + // We expect a simulate after every expression with side effects, though + // this one isn't actually needed (and wouldn't work if it were targeted). + if (instr->HasSideEffects()) { + builder->Push(instr); + builder->AddSimulate(ast_id); + builder->Pop(); + } + BuildBranch(instr); +} + + +void TestContext::BuildBranch(HValue* value) { + HGraphBuilder* builder = owner(); + HBasicBlock* materialize_true = builder->graph()->CreateBasicBlock(); + HBasicBlock* materialize_false = builder->graph()->CreateBasicBlock(); + HBranch* branch = new HBranch(materialize_true, materialize_false, value); + builder->CurrentBlock()->Finish(branch); + + HBasicBlock* true_block = if_true(); + HValue* true_value = invert_true() + ? builder->graph()->GetConstantFalse() + : builder->graph()->GetConstantTrue(); + materialize_true->set_inverted(invert_true()); + true_block->set_deopt_predecessor(materialize_true); + + if (true_block->IsInlineReturnTarget()) { + materialize_true->AddLeaveInlined(true_value, true_block); + } else { + materialize_true->last_environment()->Push(true_value); + materialize_true->Goto(true_block); + } + + HBasicBlock* false_block = if_false(); + HValue* false_value = invert_false() + ? builder->graph()->GetConstantTrue() + : builder->graph()->GetConstantFalse(); + materialize_false->set_inverted(invert_false()); + false_block->set_deopt_predecessor(materialize_false); + + if (false_block->IsInlineReturnTarget()) { + materialize_false->AddLeaveInlined(false_value, false_block); + } else { + materialize_false->last_environment()->Push(false_value); + materialize_false->Goto(false_block); + } + builder->subgraph()->set_exit_block(NULL); +} + // HGraphBuilder infrastructure for bailing out and checking bailouts. #define BAILOUT(reason) \ @@ -2061,55 +2159,14 @@ void HGraphBuilder::Bailout(const char* reason) { void HGraphBuilder::VisitForEffect(Expression* expr) { -#ifdef DEBUG - int original_count = environment()->total_count(); -#endif - BinaryOperation* binary_op = expr->AsBinaryOperation(); - - // We use special casing for expression types not handled properly by our - // usual trick of pretending they're in a value context and cleaning up - // later. - if (binary_op != NULL && binary_op->op() == Token::COMMA) { - VISIT_FOR_EFFECT(binary_op->left()); - VISIT_FOR_EFFECT(binary_op->right()); - } else { - { EffectContext for_effect(this); - Visit(expr); - } - if (HasStackOverflow() || !subgraph()->HasExit()) return; - // Discard return value. - Pop(); - // TODO(kasperl): Try to improve the way we compute the last added - // instruction. The NULL check makes me uncomfortable. - HValue* last = subgraph()->exit_block()->GetLastInstruction(); - // We need to ensure we emit a simulate after inlined functions in an - // effect context, to avoid having a bailout target the fictional - // environment with the return value on top. - if ((last != NULL && last->HasSideEffects()) || - subgraph()->exit_block()->IsInlineReturnTarget()) { - AddSimulate(expr->id()); - } - } - - ASSERT(environment()->total_count() == original_count); + EffectContext for_effect(this); + Visit(expr); } void HGraphBuilder::VisitForValue(Expression* expr) { -#ifdef DEBUG - int original_height = environment()->values()->length(); -#endif - { ValueContext for_value(this); - Visit(expr); - } - if (HasStackOverflow() || !subgraph()->HasExit()) return; - // TODO(kasperl): Try to improve the way we compute the last added - // instruction. The NULL check makes me uncomfortable. - HValue* last = subgraph()->exit_block()->GetLastInstruction(); - if (last != NULL && last->HasSideEffects()) { - AddSimulate(expr->id()); - } - ASSERT(environment()->values()->length() == original_height + 1); + ValueContext for_value(this); + Visit(expr); } @@ -2244,99 +2301,7 @@ void HGraphBuilder::VisitForControl(Expression* expr, bool invert_false) { TestContext for_test(this, true_block, false_block, invert_true, invert_false); - BinaryOperation* binary_op = expr->AsBinaryOperation(); - UnaryOperation* unary_op = expr->AsUnaryOperation(); - - if (unary_op != NULL && unary_op->op() == Token::NOT) { - VisitForControl(unary_op->expression(), - false_block, - true_block, - !invert_false, - !invert_true); - } else if (binary_op != NULL && binary_op->op() == Token::AND) { - // Translate left subexpression. - HBasicBlock* eval_right = graph()->CreateBasicBlock(); - VisitForControl(binary_op->left(), - eval_right, - false_block, - false, - invert_false); - if (HasStackOverflow()) return; - eval_right->SetJoinId(binary_op->left()->id()); - - // Translate right subexpression. - eval_right->last_environment()->Pop(); - subgraph()->set_exit_block(eval_right); - VisitForControl(binary_op->right(), - true_block, - false_block, - invert_true, - invert_false); - } else if (binary_op != NULL && binary_op->op() == Token::OR) { - // Translate left subexpression. - HBasicBlock* eval_right = graph()->CreateBasicBlock(); - VisitForControl(binary_op->left(), - true_block, - eval_right, - invert_true, - false); - if (HasStackOverflow()) return; - eval_right->SetJoinId(binary_op->left()->id()); - - // Translate right subexpression - eval_right->last_environment()->Pop(); - subgraph()->set_exit_block(eval_right); - VisitForControl(binary_op->right(), - true_block, - false_block, - invert_true, - invert_false); - } else { -#ifdef DEBUG - int original_length = environment()->values()->length(); -#endif - // TODO(kmillikin): Refactor to avoid. This code is duplicated from - // VisitForValue, except without pushing a value context on the - // expression context stack. - Visit(expr); - if (HasStackOverflow() || !subgraph()->HasExit()) return; - HValue* last = subgraph()->exit_block()->GetLastInstruction(); - if (last != NULL && last->HasSideEffects()) { - AddSimulate(expr->id()); - } - ASSERT(environment()->values()->length() == original_length + 1); - HValue* value = Pop(); - HBasicBlock* materialize_true = graph()->CreateBasicBlock(); - HBasicBlock* materialize_false = graph()->CreateBasicBlock(); - CurrentBlock()->Finish(new HBranch(materialize_true, - materialize_false, - value)); - HValue* true_value = invert_true - ? graph()->GetConstantFalse() - : graph()->GetConstantTrue(); - materialize_true->set_inverted(invert_true); - true_block->set_deopt_predecessor(materialize_true); - - if (true_block->IsInlineReturnTarget()) { - materialize_true->AddLeaveInlined(true_value, true_block); - } else { - materialize_true->last_environment()->Push(true_value); - materialize_true->Goto(true_block); - } - HValue* false_value = invert_false - ? graph()->GetConstantTrue() - : graph()->GetConstantFalse(); - materialize_false->set_inverted(invert_false); - false_block->set_deopt_predecessor(materialize_false); - - if (false_block->IsInlineReturnTarget()) { - materialize_false->AddLeaveInlined(false_value, false_block); - } else { - materialize_false->last_environment()->Push(false_value); - materialize_false->Goto(false_block); - } - subgraph()->set_exit_block(NULL); - } + Visit(expr); } @@ -2372,12 +2337,6 @@ void HGraphBuilder::PushAndAdd(HInstruction* instr) { } -void HGraphBuilder::PushAndAdd(HInstruction* instr, int position) { - instr->set_position(position); - PushAndAdd(instr); -} - - void HGraphBuilder::PushArgumentsForStubCall(int argument_count) { const int kMaxStubArguments = 4; ASSERT_GE(kMaxStubArguments, argument_count); @@ -2392,7 +2351,7 @@ void HGraphBuilder::PushArgumentsForStubCall(int argument_count) { } -void HGraphBuilder::ProcessCall(HCall* call, int source_position) { +void HGraphBuilder::ProcessCall(HCall* call) { for (int i = call->argument_count() - 1; i >= 0; --i) { HValue* value = Pop(); HPushArgument* push = new HPushArgument(value); @@ -2402,8 +2361,6 @@ void HGraphBuilder::ProcessCall(HCall* call, int source_position) { for (int i = 0; i < call->argument_count(); ++i) { AddInstruction(call->PushArgumentAt(i)); } - - PushAndAdd(call, source_position); } @@ -2914,7 +2871,9 @@ void HGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { Handle shared_info = Compiler::BuildFunctionInfo(expr, graph_->info()->script()); CHECK_BAILOUT; - PushAndAdd(new HFunctionLiteral(shared_info, expr->pretenure())); + HFunctionLiteral* instr = + new HFunctionLiteral(shared_info, expr->pretenure()); + ast_context()->ReturnInstruction(instr, expr->id()); } @@ -2935,20 +2894,21 @@ void HGraphBuilder::VisitConditional(Conditional* expr) { ADD_TO_SUBGRAPH(then_graph, expr->then_expression()); ADD_TO_SUBGRAPH(else_graph, expr->else_expression()); current_subgraph_->AppendJoin(then_graph, else_graph, expr); + ast_context()->ReturnValue(Pop()); } -void HGraphBuilder::LookupGlobalPropertyCell(VariableProxy* expr, +void HGraphBuilder::LookupGlobalPropertyCell(Variable* var, LookupResult* lookup, bool is_store) { - if (expr->is_this()) { + if (var->is_this()) { BAILOUT("global this reference"); } if (!graph()->info()->has_global_object()) { BAILOUT("no global object to optimize VariableProxy"); } Handle global(graph()->info()->global_object()); - global->Lookup(*expr->name(), lookup); + global->Lookup(*var->name(), lookup); if (!lookup->IsProperty()) { BAILOUT("global variable cell not yet introduced"); } @@ -2961,23 +2921,6 @@ void HGraphBuilder::LookupGlobalPropertyCell(VariableProxy* expr, } -void HGraphBuilder::HandleGlobalVariableLoad(VariableProxy* expr) { - LookupResult lookup; - LookupGlobalPropertyCell(expr, &lookup, false); - CHECK_BAILOUT; - - Handle global(graph()->info()->global_object()); - // TODO(3039103): Handle global property load through an IC call when access - // checks are enabled. - if (global->IsAccessCheckNeeded()) { - BAILOUT("global object requires access check"); - } - Handle cell(global->GetPropertyCell(&lookup)); - bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly(); - PushAndAdd(new HLoadGlobal(cell, check_hole)); -} - - void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { Variable* variable = expr->AsVariable(); if (variable == NULL) { @@ -2986,9 +2929,22 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { if (environment()->Lookup(variable)->CheckFlag(HValue::kIsArguments)) { BAILOUT("unsupported context for arguments object"); } - Push(environment()->Lookup(variable)); + ast_context()->ReturnValue(environment()->Lookup(variable)); } else if (variable->is_global()) { - HandleGlobalVariableLoad(expr); + LookupResult lookup; + LookupGlobalPropertyCell(variable, &lookup, false); + CHECK_BAILOUT; + + Handle global(graph()->info()->global_object()); + // TODO(3039103): Handle global property load through an IC call when access + // checks are enabled. + if (global->IsAccessCheckNeeded()) { + BAILOUT("global object requires access check"); + } + Handle cell(global->GetPropertyCell(&lookup)); + bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly(); + HLoadGlobal* instr = new HLoadGlobal(cell, check_hole); + ast_context()->ReturnInstruction(instr, expr->id()); } else { BAILOUT("reference to non-stack-allocated/non-global variable"); } @@ -2996,14 +2952,16 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { void HGraphBuilder::VisitLiteral(Literal* expr) { - PushAndAdd(new HConstant(expr->handle(), Representation::Tagged())); + HConstant* instr = new HConstant(expr->handle(), Representation::Tagged()); + ast_context()->ReturnInstruction(instr, expr->id()); } void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { - PushAndAdd(new HRegExpLiteral(expr->pattern(), - expr->flags(), - expr->literal_index())); + HRegExpLiteral* instr = new HRegExpLiteral(expr->pattern(), + expr->flags(), + expr->literal_index()); + ast_context()->ReturnInstruction(instr, expr->id()); } @@ -3012,6 +2970,8 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { expr->fast_elements(), expr->literal_index(), expr->depth())); + // The object is expected in the bailout environment during computation + // of the property values and is the value of the entire expression. PushAndAdd(literal); expr->CalculateEmitStore(); @@ -3048,6 +3008,7 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { default: UNREACHABLE(); } } + ast_context()->ReturnValue(Pop()); } @@ -3059,6 +3020,8 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { length, expr->literal_index(), expr->depth()); + // The array is expected in the bailout environment during computation + // of the property values and is the value of the entire expression. PushAndAdd(literal); HValue* elements = AddInstruction(new HLoadElements(literal)); @@ -3076,6 +3039,7 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { AddInstruction(new HStoreKeyedFastElement(elements, key, value)); AddSimulate(expr->GetIdForElement(i)); } + ast_context()->ReturnValue(Pop()); } @@ -3257,27 +3221,29 @@ void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr, Push(value); instr->set_position(expr->position()); AddInstruction(instr); - return; - } - - // Build subgraph for generic store through IC. - { - HSubgraph* subgraph = CreateBranchSubgraph(environment()); - SubgraphScope scope(this, subgraph); - if (!needs_generic && FLAG_deoptimize_uncommon_cases) { - subgraph->FinishExit(new HDeoptimize()); - } else { - HInstruction* instr = new HStoreNamedGeneric(object, name, value); - Push(value); - instr->set_position(expr->position()); - AddInstruction(instr); + if (instr->HasSideEffects()) AddSimulate(expr->id()); + } else { + // Build subgraph for generic store through IC. + { + HSubgraph* subgraph = CreateBranchSubgraph(environment()); + SubgraphScope scope(this, subgraph); + if (!needs_generic && FLAG_deoptimize_uncommon_cases) { + subgraph->FinishExit(new HDeoptimize()); + } else { + HInstruction* instr = new HStoreNamedGeneric(object, name, value); + Push(value); + instr->set_position(expr->position()); + AddInstruction(instr); + } + subgraphs.Add(subgraph); } - subgraphs.Add(subgraph); + + HBasicBlock* new_exit_block = + BuildTypeSwitch(&maps, &subgraphs, object, expr->id()); + subgraph()->set_exit_block(new_exit_block); } - HBasicBlock* new_exit_block = - BuildTypeSwitch(&maps, &subgraphs, object, expr->id()); - current_subgraph_->set_exit_block(new_exit_block); + if (subgraph()->HasExit()) ast_context()->ReturnValue(Pop()); } @@ -3333,14 +3299,20 @@ void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) { Push(value); instr->set_position(expr->position()); AddInstruction(instr); + if (instr->HasSideEffects()) AddSimulate(expr->id()); + ast_context()->ReturnValue(Pop()); } -void HGraphBuilder::HandleGlobalVariableAssignment(VariableProxy* proxy, +// Because not every expression has a position and there is not common +// superclass of Assignment and CountOperation, we cannot just pass the +// owning expression instead of position and ast_id separately. +void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var, HValue* value, - int position) { + int position, + int ast_id) { LookupResult lookup; - LookupGlobalPropertyCell(proxy, &lookup, true); + LookupGlobalPropertyCell(var, &lookup, true); CHECK_BAILOUT; Handle global(graph()->info()->global_object()); @@ -3348,6 +3320,7 @@ void HGraphBuilder::HandleGlobalVariableAssignment(VariableProxy* proxy, HInstruction* instr = new HStoreGlobal(value, cell); instr->set_position(position); AddInstruction(instr); + if (instr->HasSideEffects()) AddSimulate(ast_id); } @@ -3371,10 +3344,12 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { VISIT_FOR_VALUE(operation); if (var->is_global()) { - HandleGlobalVariableAssignment(proxy, Top(), expr->position()); + HandleGlobalVariableAssignment(var, Top(), expr->position(), expr->id()); } else { Bind(var, Top()); } + ast_context()->ReturnValue(Pop()); + } else if (prop != NULL) { prop->RecordTypeFeedback(oracle()); @@ -3392,9 +3367,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { load = BuildLoadNamedGeneric(obj, prop); } PushAndAdd(load); - if (load->HasSideEffects()) { - AddSimulate(expr->compound_bailout_id()); - } + if (load->HasSideEffects()) AddSimulate(expr->compound_bailout_id()); VISIT_FOR_VALUE(expr->value()); HValue* right = Pop(); @@ -3406,10 +3379,11 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { HInstruction* store = BuildStoreNamed(obj, instr, prop); AddInstruction(store); + if (store->HasSideEffects()) AddSimulate(expr->id()); - // Drop the simulated receiver and value and put back the value. + // Drop the simulated receiver and value. Return the value. Drop(2); - Push(instr); + ast_context()->ReturnValue(instr); } else { // Keyed property. @@ -3425,9 +3399,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { ? BuildLoadKeyedFastElement(obj, key, prop) : BuildLoadKeyedGeneric(obj, key); PushAndAdd(load); - if (load->HasSideEffects()) { - AddSimulate(expr->compound_bailout_id()); - } + if (load->HasSideEffects()) AddSimulate(expr->compound_bailout_id()); VISIT_FOR_VALUE(expr->value()); HValue* right = Pop(); @@ -3441,11 +3413,13 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { ? BuildStoreKeyedFastElement(obj, key, instr, prop) : BuildStoreKeyedGeneric(obj, key, instr); AddInstruction(store); + if (store->HasSideEffects()) AddSimulate(expr->id()); - // Drop the simulated receiver, key and value and put back the value. + // Drop the simulated receiver, key, and value. Return the value. Drop(3); - Push(instr); + ast_context()->ReturnValue(instr); } + } else { BAILOUT("invalid lhs in compound assignment"); } @@ -3465,9 +3439,11 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { if (var != NULL) { if (proxy->IsArguments()) BAILOUT("assignment to arguments"); + + // Handle the assignment. if (var->is_global()) { VISIT_FOR_VALUE(expr->value()); - HandleGlobalVariableAssignment(proxy, Top(), expr->position()); + HandleGlobalVariableAssignment(var, Top(), expr->position(), expr->id()); } else { // We allow reference to the arguments object only in assignemtns // to local variables to make sure that the arguments object does @@ -3480,9 +3456,11 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { } else { VISIT_FOR_VALUE(expr->value()); } - Bind(proxy->var(), Top()); } + // Return the value. + ast_context()->ReturnValue(Pop()); + } else if (prop != NULL) { HandlePropertyAssignment(expr); } else { @@ -3492,6 +3470,10 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { void HGraphBuilder::VisitThrow(Throw* expr) { + // We don't optimize functions with invalid left-hand sides in + // assignments, count operations, or for-in. Consequently throw can + // currently only occur in an effect context. + ASSERT(ast_context()->IsEffect()); VISIT_FOR_VALUE(expr->exception()); HValue* value = environment()->Pop(); @@ -3525,7 +3507,8 @@ void HGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr, SubgraphScope scope(this, subgraph); HInstruction* instr = BuildLoadNamedField(object, expr, map, &lookup, false); - PushAndAdd(instr, expr->position()); + instr->set_position(expr->position()); + PushAndAdd(instr); subgraphs.Add(subgraph); } else { needs_generic = true; @@ -3536,26 +3519,30 @@ void HGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr, // generic load. if (maps.length() == 0) { HInstruction* instr = BuildLoadNamedGeneric(object, expr); - PushAndAdd(instr, expr->position()); - return; - } - - // Build subgraph for generic load through IC. - { - HSubgraph* subgraph = CreateBranchSubgraph(environment()); - SubgraphScope scope(this, subgraph); - if (!needs_generic && FLAG_deoptimize_uncommon_cases) { - subgraph->FinishExit(new HDeoptimize()); - } else { - HInstruction* instr = BuildLoadNamedGeneric(object, expr); - PushAndAdd(instr, expr->position()); + instr->set_position(expr->position()); + PushAndAdd(instr); + if (instr->HasSideEffects()) AddSimulate(expr->id()); + } else { + // Build subgraph for generic load through IC. + { + HSubgraph* subgraph = CreateBranchSubgraph(environment()); + SubgraphScope scope(this, subgraph); + if (!needs_generic && FLAG_deoptimize_uncommon_cases) { + subgraph->FinishExit(new HDeoptimize()); + } else { + HInstruction* instr = BuildLoadNamedGeneric(object, expr); + instr->set_position(expr->position()); + PushAndAdd(instr); + } + subgraphs.Add(subgraph); } - subgraphs.Add(subgraph); + + HBasicBlock* new_exit_block = + BuildTypeSwitch(&maps, &subgraphs, object, expr->id()); + subgraph()->set_exit_block(new_exit_block); } - HBasicBlock* new_exit_block = - BuildTypeSwitch(&maps, &subgraphs, object, expr->id()); - current_subgraph_->set_exit_block(new_exit_block); + if (subgraph()->HasExit()) ast_context()->ReturnValue(Pop()); } @@ -3668,11 +3655,12 @@ bool HGraphBuilder::TryArgumentsAccess(Property* expr) { return false; } + HInstruction* result = NULL; if (expr->key()->IsPropertyName()) { Handle name = expr->key()->AsLiteral()->AsPropertyName(); if (!name->IsEqualTo(CStrVector("length"))) return false; HInstruction* elements = AddInstruction(new HArgumentsElements); - PushAndAdd(new HArgumentsLength(elements)); + result = new HArgumentsLength(elements); } else { VisitForValue(expr->key()); if (HasStackOverflow()) return false; @@ -3680,8 +3668,9 @@ bool HGraphBuilder::TryArgumentsAccess(Property* expr) { HInstruction* elements = AddInstruction(new HArgumentsElements); HInstruction* length = AddInstruction(new HArgumentsLength(elements)); AddInstruction(new HBoundsCheck(key, length)); - PushAndAdd(new HAccessArgumentsAt(elements, length, key)); + result = new HAccessArgumentsAt(elements, length, key); } + ast_context()->ReturnInstruction(result, expr->id()); return true; } @@ -3728,7 +3717,8 @@ void HGraphBuilder::VisitProperty(Property* expr) { ? BuildLoadKeyedFastElement(obj, key, expr) : BuildLoadKeyedGeneric(obj, key); } - PushAndAdd(instr, expr->position()); + instr->set_position(expr->position()); + ast_context()->ReturnInstruction(instr, expr->id()); } @@ -3763,9 +3753,9 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, // Build subgraphs for each of the specific maps. // - // TODO(ager): We should recognize when the prototype chains for - // different maps are identical. In that case we can avoid - // repeatedly generating the same prototype map checks. + // TODO(ager): We should recognize when the prototype chains for different + // maps are identical. In that case we can avoid repeatedly generating the + // same prototype map checks. for (int i = 0; i < number_of_types; ++i) { Handle map = types->at(i); if (expr->ComputeTarget(map, name)) { @@ -3782,7 +3772,9 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, // during hydrogen processing. CHECK_BAILOUT; HCall* call = new HCallConstantFunction(expr->target(), argument_count); - ProcessCall(call, expr->position()); + call->set_position(expr->position()); + ProcessCall(call); + PushAndAdd(call); } subgraphs.Add(subgraph); } else { @@ -3790,30 +3782,34 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, } } - // If we couldn't compute the target for any of the maps just - // perform an IC call. + // If we couldn't compute the target for any of the maps just perform an + // IC call. if (maps.length() == 0) { HCall* call = new HCallNamed(name, argument_count); - ProcessCall(call, expr->position()); - return; - } - - // Build subgraph for generic call through IC. - { - HSubgraph* subgraph = CreateBranchSubgraph(environment()); - SubgraphScope scope(this, subgraph); - if (!needs_generic && FLAG_deoptimize_uncommon_cases) { - subgraph->FinishExit(new HDeoptimize()); - } else { - HCall* call = new HCallNamed(name, argument_count); - ProcessCall(call, expr->position()); + call->set_position(expr->position()); + ProcessCall(call); + ast_context()->ReturnInstruction(call, expr->id()); + } else { + // Build subgraph for generic call through IC. + { + HSubgraph* subgraph = CreateBranchSubgraph(environment()); + SubgraphScope scope(this, subgraph); + if (!needs_generic && FLAG_deoptimize_uncommon_cases) { + subgraph->FinishExit(new HDeoptimize()); + } else { + HCall* call = new HCallNamed(name, argument_count); + call->set_position(expr->position()); + ProcessCall(call); + PushAndAdd(call); + } + subgraphs.Add(subgraph); } - subgraphs.Add(subgraph); - } - HBasicBlock* new_exit_block = - BuildTypeSwitch(&maps, &subgraphs, receiver, expr->id()); - current_subgraph_->set_exit_block(new_exit_block); + HBasicBlock* new_exit_block = + BuildTypeSwitch(&maps, &subgraphs, receiver, expr->id()); + subgraph()->set_exit_block(new_exit_block); + if (new_exit_block != NULL) ast_context()->ReturnValue(Pop()); + } } @@ -4061,6 +4057,7 @@ bool HGraphBuilder::TryInline(Call* expr) { function_return_ = saved_function_return; oracle_ = saved_oracle; graph()->info()->SetOsrAstId(saved_osr_ast_id); + return true; } @@ -4086,10 +4083,10 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) { case kMathSqrt: if (argument_count == 2) { HValue* argument = Pop(); - // Pop receiver. - Pop(); + Drop(1); // Receiver. HUnaryMathOperation* op = new HUnaryMathOperation(argument, id); - PushAndAdd(op, expr->position()); + op->set_position(expr->position()); + ast_context()->ReturnInstruction(op, expr->id()); return true; } break; @@ -4098,11 +4095,13 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) { HValue* right = Pop(); HValue* left = Pop(); Pop(); // Pop receiver. + HInstruction* result = NULL; // Use sqrt() if exponent is 0.5 or -0.5. if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) { double exponent = HConstant::cast(right)->DoubleValue(); if (exponent == 0.5) { - PushAndAdd(new HUnaryMathOperation(left, kMathPowHalf)); + result = new HUnaryMathOperation(left, kMathPowHalf); + ast_context()->ReturnInstruction(result, expr->id()); return true; } else if (exponent == -0.5) { HConstant* double_one = @@ -4112,20 +4111,27 @@ bool HGraphBuilder::TryMathFunctionInline(Call* expr) { HUnaryMathOperation* square_root = new HUnaryMathOperation(left, kMathPowHalf); AddInstruction(square_root); - PushAndAdd(new HDiv(double_one, square_root)); + // MathPowHalf doesn't have side effects so there's no need for + // an environment simulation here. + ASSERT(!square_root->HasSideEffects()); + result = new HDiv(double_one, square_root); + ast_context()->ReturnInstruction(result, expr->id()); return true; } else if (exponent == 2.0) { - PushAndAdd(new HMul(left, left)); + result = new HMul(left, left); + ast_context()->ReturnInstruction(result, expr->id()); return true; } } else if (right->IsConstant() && HConstant::cast(right)->HasInteger32Value() && HConstant::cast(right)->Integer32Value() == 2) { - PushAndAdd(new HMul(left, left)); + result = new HMul(left, left); + ast_context()->ReturnInstruction(result, expr->id()); return true; } - PushAndAdd(new HPower(left, right)); + result = new HPower(left, right); + ast_context()->ReturnInstruction(result, expr->id()); return true; } break; @@ -4170,8 +4176,10 @@ bool HGraphBuilder::TryCallApply(Call* expr) { function, expr->GetReceiverTypes()->first(), true); - PushAndAdd(new HApplyArguments(function, receiver, length, elements), - expr->position()); + HInstruction* result = + new HApplyArguments(function, receiver, length, elements); + result->set_position(expr->position()); + ast_context()->ReturnInstruction(result, expr->id()); return true; } @@ -4199,12 +4207,10 @@ void HGraphBuilder::VisitCall(Call* expr) { CHECK_BAILOUT; call = new HCallKeyed(key, argument_count); - ProcessCall(call, expr->position()); - HValue* result = Pop(); - // Drop the receiver from the environment and put back the result of - // the call. - Drop(1); - Push(result); + call->set_position(expr->position()); + ProcessCall(call); + Drop(1); // Key. + ast_context()->ReturnInstruction(call, expr->id()); return; } @@ -4227,7 +4233,19 @@ void HGraphBuilder::VisitCall(Call* expr) { if (expr->IsMonomorphic()) { AddCheckConstantFunction(expr, receiver, types->first(), true); - if (TryMathFunctionInline(expr) || TryInline(expr)) { + if (TryMathFunctionInline(expr)) { + return; + } else if (TryInline(expr)) { + if (subgraph()->HasExit()) { + HValue* return_value = Pop(); + // If we inlined a function in a test context then we need to emit + // a simulate here to shadow the ones at the end of the + // predecessor blocks. Those environments contain the return + // value on top and do not correspond to any actual state of the + // unoptimized code. + if (ast_context()->IsEffect()) AddSimulate(expr->id()); + ast_context()->ReturnValue(return_value); + } return; } else { // Check for bailout, as the TryInline call in the if condition above @@ -4235,6 +4253,7 @@ void HGraphBuilder::VisitCall(Call* expr) { CHECK_BAILOUT; call = new HCallConstantFunction(expr->target(), argument_count); } + } else if (types != NULL && types->length() > 1) { HandlePolymorphicCallNamed(expr, receiver, types, name); return; @@ -4282,7 +4301,19 @@ void HGraphBuilder::VisitCall(Call* expr) { IsGlobalObject()); environment()->SetExpressionStackAt(receiver_index, global_receiver); - if (TryInline(expr)) return; + if (TryInline(expr)) { + if (subgraph()->HasExit()) { + HValue* return_value = Pop(); + // If we inlined a function in a test context then we need to + // emit a simulate here to shadow the ones at the end of the + // predecessor blocks. Those environments contain the return + // value on top and do not correspond to any actual state of the + // unoptimized code. + if (ast_context()->IsEffect()) AddSimulate(expr->id()); + ast_context()->ReturnValue(return_value); + } + return; + } // Check for bailout, as trying to inline might fail due to bailout // during hydrogen processing. CHECK_BAILOUT; @@ -4305,7 +4336,9 @@ void HGraphBuilder::VisitCall(Call* expr) { } } - ProcessCall(call, expr->position()); + call->set_position(expr->position()); + ProcessCall(call); + ast_context()->ReturnInstruction(call, expr->id()); } @@ -4319,8 +4352,9 @@ void HGraphBuilder::VisitCallNew(CallNew* expr) { int argument_count = expr->arguments()->length() + 1; // Plus constructor. HCall* call = new HCallNew(argument_count); - - ProcessCall(call, expr->position()); + call->set_position(expr->position()); + ProcessCall(call); + ast_context()->ReturnInstruction(call, expr->id()); } @@ -4328,7 +4362,7 @@ void HGraphBuilder::VisitCallNew(CallNew* expr) { // Lookup table for generators for runtime calls that are generated inline. // Elements of the table are member pointers to functions of HGraphBuilder. -#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \ +#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize) \ &HGraphBuilder::Generate##Name, const HGraphBuilder::InlineFunctionGenerator @@ -4342,7 +4376,7 @@ const HGraphBuilder::InlineFunctionGenerator void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) { Handle name = expr->name(); if (name->IsEqualTo(CStrVector("_Log"))) { - Push(graph()->GetConstantUndefined()); + ast_context()->ReturnValue(graph()->GetConstantUndefined()); return; } @@ -4368,11 +4402,13 @@ void HGraphBuilder::VisitCallRuntime(CallRuntime* expr) { InlineFunctionGenerator generator = kInlineFunctionGenerators[lookup_index]; // Call the inline code generator using the pointer-to-member. - (this->*generator)(argument_count); + (this->*generator)(argument_count, expr->id()); } else { ASSERT(function->intrinsic_type == Runtime::RUNTIME); HCall* call = new HCallRuntime(name, expr->function(), argument_count); - ProcessCall(call, RelocInfo::kNoPosition); + call->set_position(RelocInfo::kNoPosition); + ProcessCall(call); + ast_context()->ReturnInstruction(call, expr->id()); } } @@ -4381,7 +4417,7 @@ void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) { Token::Value op = expr->op(); if (op == Token::VOID) { VISIT_FOR_EFFECT(expr->expression()); - Push(graph()->GetConstantUndefined()); + ast_context()->ReturnValue(graph()->GetConstantUndefined()); } else if (op == Token::DELETE) { Property* prop = expr->expression()->AsProperty(); Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); @@ -4389,36 +4425,47 @@ void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) { // Result of deleting non-property, non-variable reference is true. // Evaluate the subexpression for side effects. VISIT_FOR_EFFECT(expr->expression()); - Push(graph_->GetConstantTrue()); + ast_context()->ReturnValue(graph()->GetConstantTrue()); } else if (var != NULL && !var->is_global() && var->AsSlot() != NULL && var->AsSlot()->type() != Slot::LOOKUP) { // Result of deleting non-global, non-dynamic variables is false. // The subexpression does not have side effects. - Push(graph_->GetConstantFalse()); + ast_context()->ReturnValue(graph()->GetConstantFalse()); } else if (prop != NULL) { VISIT_FOR_VALUE(prop->obj()); VISIT_FOR_VALUE(prop->key()); HValue* key = Pop(); HValue* obj = Pop(); - PushAndAdd(new HDeleteProperty(obj, key)); + ast_context()->ReturnInstruction(new HDeleteProperty(obj, key), + expr->id()); } else if (var->is_global()) { BAILOUT("delete with global variable"); } else { BAILOUT("delete with non-global variable"); } } else if (op == Token::NOT) { - HSubgraph* true_graph = CreateEmptySubgraph(); - HSubgraph* false_graph = CreateEmptySubgraph(); - VisitCondition(expr->expression(), - false_graph->entry_block(), - true_graph->entry_block(), - true, true); - if (HasStackOverflow()) return; - true_graph->environment()->Push(graph_->GetConstantTrue()); - false_graph->environment()->Push(graph_->GetConstantFalse()); - current_subgraph_->AppendJoin(true_graph, false_graph, expr); + if (ast_context()->IsTest()) { + TestContext* context = TestContext::cast(ast_context()); + VisitForControl(expr->expression(), + context->if_false(), + context->if_true(), + !context->invert_false(), + !context->invert_true()); + } else { + HSubgraph* true_graph = CreateEmptySubgraph(); + HSubgraph* false_graph = CreateEmptySubgraph(); + VisitCondition(expr->expression(), + false_graph->entry_block(), + true_graph->entry_block(), + true, true); + if (HasStackOverflow()) return; + true_graph->environment()->Push(graph_->GetConstantTrue()); + false_graph->environment()->Push(graph_->GetConstantFalse()); + current_subgraph_->AppendJoin(true_graph, false_graph, expr); + ast_context()->ReturnValue(Pop()); + } } else if (op == Token::BIT_NOT || op == Token::SUB) { VISIT_FOR_VALUE(expr->expression()); HValue* value = Pop(); @@ -4434,11 +4481,11 @@ void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) { UNREACHABLE(); break; } - PushAndAdd(instr); + ast_context()->ReturnInstruction(instr, expr->id()); } else if (op == Token::TYPEOF) { VISIT_FOR_VALUE(expr->expression()); HValue* value = Pop(); - PushAndAdd(new HTypeof(value)); + ast_context()->ReturnInstruction(new HTypeof(value), expr->id()); } else { BAILOUT("Value: unsupported unary operation"); } @@ -4489,11 +4536,12 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { } if (var->is_global()) { - HandleGlobalVariableAssignment(proxy, instr, expr->position()); + HandleGlobalVariableAssignment(var, instr, expr->position(), expr->id()); } else { ASSERT(var->IsStackAllocated()); Bind(var, instr); } + ast_context()->ReturnValue(Pop()); } else if (prop != NULL) { prop->RecordTypeFeedback(oracle()); @@ -4501,8 +4549,8 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { if (prop->key()->IsPropertyName()) { // Named property. - // Match the full code generator stack by simulate an extra stack element - // for postfix operations in a value context. + // Match the full code generator stack by simulating an extra stack + // element for postfix operations in a value context. if (expr->is_postfix() && !ast_context()->IsEffect()) { Push(graph_->GetConstantUndefined()); } @@ -4523,6 +4571,8 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { HValue* value = Pop(); + // There is no deoptimization to after the increment, so we don't need + // to simulate the expression stack after this instruction. HInstruction* instr = BuildIncrement(value, inc); AddInstruction(instr); @@ -4530,8 +4580,6 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { AddInstruction(store); // Drop simulated receiver and push the result. - // There is no deoptimization to after the increment, so we can simulate - // the expression stack here. Drop(1); if (expr->is_prefix()) { Push(instr); @@ -4540,6 +4588,9 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { Push(value); } + if (store->HasSideEffects()) AddSimulate(expr->id()); + ast_context()->ReturnValue(Pop()); + } else { // Keyed property. @@ -4566,6 +4617,8 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { HValue* value = Pop(); + // There is no deoptimization to after the increment, so we don't need + // to simulate the expression stack after this instruction. HInstruction* instr = BuildIncrement(value, inc); AddInstruction(instr); @@ -4575,8 +4628,6 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { AddInstruction(store); // Drop simulated receiver and key and push the result. - // There is no deoptimization to after the increment, so we can simulate - // the expression stack here. Drop(2); if (expr->is_prefix()) { Push(instr); @@ -4584,7 +4635,11 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { if (!ast_context()->IsEffect()) Drop(1); // Drop simulated zero. Push(value); } + + if (store->HasSideEffects()) AddSimulate(expr->id()); + ast_context()->ReturnValue(Pop()); } + } else { BAILOUT("invalid lhs in count operation"); } @@ -4666,21 +4721,47 @@ static bool IsClassOfTest(CompareOperation* expr) { void HGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) { if (expr->op() == Token::COMMA) { VISIT_FOR_EFFECT(expr->left()); - VISIT_FOR_VALUE(expr->right()); - } else if (expr->op() == Token::AND || expr->op() == Token::OR) { - VISIT_FOR_VALUE(expr->left()); - ASSERT(current_subgraph_->HasExit()); + // Visit the right subexpression in the same AST context as the entire + // expression. + Visit(expr->right()); - HValue* left = Top(); + } else if (expr->op() == Token::AND || expr->op() == Token::OR) { bool is_logical_and = (expr->op() == Token::AND); + if (ast_context()->IsTest()) { + TestContext* context = TestContext::cast(ast_context()); + // Translate left subexpression. + HBasicBlock* eval_right = graph()->CreateBasicBlock(); + if (is_logical_and) { + VisitForControl(expr->left(), eval_right, context->if_false(), + false, context->invert_false()); + } else { + VisitForControl(expr->left(), context->if_true(), eval_right, + context->invert_true(), false); + } + if (HasStackOverflow()) return; + eval_right->SetJoinId(expr->left()->id()); + + // Translate right subexpression by visiting it in the same AST + // context as the entire expression. + eval_right->last_environment()->Pop(); + subgraph()->set_exit_block(eval_right); + Visit(expr->right()); + + } else { + VISIT_FOR_VALUE(expr->left()); + ASSERT(current_subgraph_->HasExit()); + + HValue* left = Top(); + HEnvironment* environment_copy = environment()->Copy(); + environment_copy->Pop(); + HSubgraph* right_subgraph; + right_subgraph = CreateBranchSubgraph(environment_copy); + ADD_TO_SUBGRAPH(right_subgraph, expr->right()); + current_subgraph_->AppendOptional(right_subgraph, is_logical_and, left); + current_subgraph_->exit_block()->SetJoinId(expr->id()); + ast_context()->ReturnValue(Pop()); + } - HEnvironment* environment_copy = environment()->Copy(); - environment_copy->Pop(); - HSubgraph* right_subgraph; - right_subgraph = CreateBranchSubgraph(environment_copy); - ADD_TO_SUBGRAPH(right_subgraph, expr->right()); - current_subgraph_->AppendOptional(right_subgraph, is_logical_and, left); - current_subgraph_->exit_block()->SetJoinId(expr->id()); } else { VISIT_FOR_VALUE(expr->left()); VISIT_FOR_VALUE(expr->right()); @@ -4688,7 +4769,8 @@ void HGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) { HValue* right = Pop(); HValue* left = Pop(); HInstruction* instr = BuildBinaryOperation(expr, left, right); - PushAndAdd(instr, expr->position()); + instr->set_position(expr->position()); + ast_context()->ReturnInstruction(instr, expr->id()); } } @@ -4727,7 +4809,8 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { Literal* literal = expr->right()->AsLiteral(); Handle rhs = Handle::cast(literal->handle()); HInstruction* instr = new HClassOfTest(value, rhs); - PushAndAdd(instr, expr->position()); + instr->set_position(expr->position()); + ast_context()->ReturnInstruction(instr, expr->id()); return; } @@ -4741,7 +4824,8 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { HValue* left = Pop(); HInstruction* instr = new HTypeofIs(left, Handle::cast(right_literal->handle())); - PushAndAdd(instr, expr->position()); + instr->set_position(expr->position()); + ast_context()->ReturnInstruction(instr, expr->id()); return; } @@ -4777,7 +4861,8 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { compare->SetInputRepresentation(r); instr = compare; } - PushAndAdd(instr, expr->position()); + instr->set_position(expr->position()); + ast_context()->ReturnInstruction(instr, expr->id()); } @@ -4786,8 +4871,7 @@ void HGraphBuilder::VisitCompareToNull(CompareToNull* expr) { HValue* value = Pop(); HIsNull* compare = new HIsNull(value, expr->is_strict()); - - PushAndAdd(compare); + ast_context()->ReturnInstruction(compare, expr->id()); } @@ -4814,301 +4898,305 @@ void HGraphBuilder::VisitDeclaration(Declaration* decl) { // Generators for inline runtime functions. // Support for types. -void HGraphBuilder::GenerateIsSmi(int argument_count) { +void HGraphBuilder::GenerateIsSmi(int argument_count, int ast_id) { ASSERT(argument_count == 1); - HValue* value = Pop(); - PushAndAdd(new HIsSmi(value)); + HIsSmi* result = new HIsSmi(value); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateIsSpecObject(int argument_count) { +void HGraphBuilder::GenerateIsSpecObject(int argument_count, int ast_id) { ASSERT(argument_count == 1); - HValue* value = Pop(); - HHasInstanceType* test = + HHasInstanceType* result = new HHasInstanceType(value, FIRST_JS_OBJECT_TYPE, LAST_TYPE); - PushAndAdd(test); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateIsFunction(int argument_count) { +void HGraphBuilder::GenerateIsFunction(int argument_count, int ast_id) { ASSERT(argument_count == 1); - HValue* value = Pop(); - HHasInstanceType* test = - new HHasInstanceType(value, JS_FUNCTION_TYPE); - PushAndAdd(test); + HHasInstanceType* result = new HHasInstanceType(value, JS_FUNCTION_TYPE); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateHasCachedArrayIndex(int argument_count) { +void HGraphBuilder::GenerateHasCachedArrayIndex(int argument_count, + int ast_id) { ASSERT(argument_count == 1); - HValue* value = Pop(); - HHasCachedArrayIndex* spec_test = new HHasCachedArrayIndex(value); - PushAndAdd(spec_test); + HHasCachedArrayIndex* result = new HHasCachedArrayIndex(value); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateIsArray(int argument_count) { +void HGraphBuilder::GenerateIsArray(int argument_count, int ast_id) { ASSERT(argument_count == 1); - HValue* value = Pop(); - HHasInstanceType* test = - new HHasInstanceType(value, JS_ARRAY_TYPE); - PushAndAdd(test); + HHasInstanceType* result = new HHasInstanceType(value, JS_ARRAY_TYPE); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateIsRegExp(int argument_count) { +void HGraphBuilder::GenerateIsRegExp(int argument_count, int ast_id) { ASSERT(argument_count == 1); - HValue* value = Pop(); - HHasInstanceType* test = - new HHasInstanceType(value, JS_REGEXP_TYPE); - PushAndAdd(test); + HHasInstanceType* result = new HHasInstanceType(value, JS_REGEXP_TYPE); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateIsNonNegativeSmi(int argument_count) { +void HGraphBuilder::GenerateIsNonNegativeSmi(int argument_count, + int ast_id) { BAILOUT("inlined runtime function: IsNonNegativeSmi"); } -void HGraphBuilder::GenerateIsObject(int argument_count) { +void HGraphBuilder::GenerateIsObject(int argument_count, int ast_id) { BAILOUT("inlined runtime function: IsObject"); } -void HGraphBuilder::GenerateIsUndetectableObject(int argument_count) { +void HGraphBuilder::GenerateIsUndetectableObject(int argument_count, + int ast_id) { BAILOUT("inlined runtime function: IsUndetectableObject"); } void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf( - int argument_count) { + int argument_count, + int ast_id) { BAILOUT("inlined runtime function: IsStringWrapperSafeForDefaultValueOf"); } // Support for construct call checks. -void HGraphBuilder::GenerateIsConstructCall(int argument_count) { +void HGraphBuilder::GenerateIsConstructCall(int argument_count, int ast_id) { BAILOUT("inlined runtime function: IsConstructCall"); } // Support for arguments.length and arguments[?]. -void HGraphBuilder::GenerateArgumentsLength(int argument_count) { +void HGraphBuilder::GenerateArgumentsLength(int argument_count, int ast_id) { ASSERT(argument_count == 0); HInstruction* elements = AddInstruction(new HArgumentsElements); - PushAndAdd(new HArgumentsLength(elements)); + HArgumentsLength* result = new HArgumentsLength(elements); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateArguments(int argument_count) { +void HGraphBuilder::GenerateArguments(int argument_count, int ast_id) { ASSERT(argument_count == 1); HValue* index = Pop(); HInstruction* elements = AddInstruction(new HArgumentsElements); HInstruction* length = AddInstruction(new HArgumentsLength(elements)); - PushAndAdd(new HAccessArgumentsAt(elements, length, index)); + HAccessArgumentsAt* result = new HAccessArgumentsAt(elements, length, index); + ast_context()->ReturnInstruction(result, ast_id); } // Support for accessing the class and value fields of an object. -void HGraphBuilder::GenerateClassOf(int argument_count) { +void HGraphBuilder::GenerateClassOf(int argument_count, int ast_id) { // The special form detected by IsClassOfTest is detected before we get here // and does not cause a bailout. BAILOUT("inlined runtime function: ClassOf"); } -void HGraphBuilder::GenerateValueOf(int argument_count) { +void HGraphBuilder::GenerateValueOf(int argument_count, int ast_id) { ASSERT(argument_count == 1); - HValue* value = Pop(); - HValueOf* op = new HValueOf(value); - PushAndAdd(op); + HValueOf* result = new HValueOf(value); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateSetValueOf(int argument_count) { +void HGraphBuilder::GenerateSetValueOf(int argument_count, int ast_id) { BAILOUT("inlined runtime function: SetValueOf"); } // Fast support for charCodeAt(n). -void HGraphBuilder::GenerateStringCharCodeAt(int argument_count) { +void HGraphBuilder::GenerateStringCharCodeAt(int argument_count, int ast_id) { BAILOUT("inlined runtime function: StringCharCodeAt"); } // Fast support for string.charAt(n) and string[n]. -void HGraphBuilder::GenerateStringCharFromCode(int argument_count) { +void HGraphBuilder::GenerateStringCharFromCode(int argument_count, + int ast_id) { BAILOUT("inlined runtime function: StringCharFromCode"); } // Fast support for string.charAt(n) and string[n]. -void HGraphBuilder::GenerateStringCharAt(int argument_count) { +void HGraphBuilder::GenerateStringCharAt(int argument_count, int ast_id) { ASSERT_EQ(2, argument_count); PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::StringCharAt, argument_count), - RelocInfo::kNoPosition); + HCallStub* result = new HCallStub(CodeStub::StringCharAt, argument_count); + ast_context()->ReturnInstruction(result, ast_id); } // Fast support for object equality testing. -void HGraphBuilder::GenerateObjectEquals(int argument_count) { +void HGraphBuilder::GenerateObjectEquals(int argument_count, int ast_id) { ASSERT(argument_count == 2); - HValue* right = Pop(); HValue* left = Pop(); - PushAndAdd(new HCompareJSObjectEq(left, right)); + HCompareJSObjectEq* result = new HCompareJSObjectEq(left, right); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateLog(int argument_count) { +void HGraphBuilder::GenerateLog(int argument_count, int ast_id) { UNREACHABLE(); // We caught this in VisitCallRuntime. } // Fast support for Math.random(). -void HGraphBuilder::GenerateRandomHeapNumber(int argument_count) { +void HGraphBuilder::GenerateRandomHeapNumber(int argument_count, int ast_id) { BAILOUT("inlined runtime function: RandomHeapNumber"); } // Fast support for StringAdd. -void HGraphBuilder::GenerateStringAdd(int argument_count) { +void HGraphBuilder::GenerateStringAdd(int argument_count, int ast_id) { ASSERT_EQ(2, argument_count); PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::StringAdd, argument_count), - RelocInfo::kNoPosition); + HCallStub* result = new HCallStub(CodeStub::StringAdd, argument_count); + ast_context()->ReturnInstruction(result, ast_id); } // Fast support for SubString. -void HGraphBuilder::GenerateSubString(int argument_count) { +void HGraphBuilder::GenerateSubString(int argument_count, int ast_id) { ASSERT_EQ(3, argument_count); PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::SubString, argument_count), - RelocInfo::kNoPosition); + HCallStub* result = new HCallStub(CodeStub::SubString, argument_count); + ast_context()->ReturnInstruction(result, ast_id); } // Fast support for StringCompare. -void HGraphBuilder::GenerateStringCompare(int argument_count) { +void HGraphBuilder::GenerateStringCompare(int argument_count, int ast_id) { ASSERT_EQ(2, argument_count); PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::StringCompare, argument_count), - RelocInfo::kNoPosition); + HCallStub* result = new HCallStub(CodeStub::StringCompare, argument_count); + ast_context()->ReturnInstruction(result, ast_id); } // Support for direct calls from JavaScript to native RegExp code. -void HGraphBuilder::GenerateRegExpExec(int argument_count) { +void HGraphBuilder::GenerateRegExpExec(int argument_count, int ast_id) { ASSERT_EQ(4, argument_count); PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::RegExpExec, argument_count), - RelocInfo::kNoPosition); + HCallStub* result = new HCallStub(CodeStub::RegExpExec, argument_count); + ast_context()->ReturnInstruction(result, ast_id); } // Construct a RegExp exec result with two in-object properties. -void HGraphBuilder::GenerateRegExpConstructResult(int argument_count) { +void HGraphBuilder::GenerateRegExpConstructResult(int argument_count, + int ast_id) { ASSERT_EQ(3, argument_count); PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::RegExpConstructResult, argument_count), - RelocInfo::kNoPosition); + HCallStub* result = + new HCallStub(CodeStub::RegExpConstructResult, argument_count); + ast_context()->ReturnInstruction(result, ast_id); } // Support for fast native caches. -void HGraphBuilder::GenerateGetFromCache(int argument_count) { +void HGraphBuilder::GenerateGetFromCache(int argument_count, int ast_id) { BAILOUT("inlined runtime function: GetFromCache"); } // Fast support for number to string. -void HGraphBuilder::GenerateNumberToString(int argument_count) { +void HGraphBuilder::GenerateNumberToString(int argument_count, int ast_id) { ASSERT_EQ(1, argument_count); PushArgumentsForStubCall(argument_count); - PushAndAdd(new HCallStub(CodeStub::NumberToString, argument_count), - RelocInfo::kNoPosition); + HCallStub* result = new HCallStub(CodeStub::NumberToString, argument_count); + ast_context()->ReturnInstruction(result, ast_id); } // Fast swapping of elements. Takes three expressions, the object and two // indices. This should only be used if the indices are known to be // non-negative and within bounds of the elements array at the call site. -void HGraphBuilder::GenerateSwapElements(int argument_count) { +void HGraphBuilder::GenerateSwapElements(int argument_count, int ast_id) { BAILOUT("inlined runtime function: SwapElements"); } // Fast call for custom callbacks. -void HGraphBuilder::GenerateCallFunction(int argument_count) { +void HGraphBuilder::GenerateCallFunction(int argument_count, int ast_id) { BAILOUT("inlined runtime function: CallFunction"); } // Fast call to math functions. -void HGraphBuilder::GenerateMathPow(int argument_count) { +void HGraphBuilder::GenerateMathPow(int argument_count, int ast_id) { ASSERT_EQ(2, argument_count); HValue* right = Pop(); HValue* left = Pop(); - PushAndAdd(new HPower(left, right)); + HPower* result = new HPower(left, right); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateMathSin(int argument_count) { +void HGraphBuilder::GenerateMathSin(int argument_count, int ast_id) { ASSERT_EQ(1, argument_count); PushArgumentsForStubCall(argument_count); - HCallStub* instr = + HCallStub* result = new HCallStub(CodeStub::TranscendentalCache, argument_count); - instr->set_transcendental_type(TranscendentalCache::SIN); - PushAndAdd(instr, RelocInfo::kNoPosition); + result->set_transcendental_type(TranscendentalCache::SIN); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateMathCos(int argument_count) { +void HGraphBuilder::GenerateMathCos(int argument_count, int ast_id) { ASSERT_EQ(1, argument_count); PushArgumentsForStubCall(argument_count); - HCallStub* instr = + HCallStub* result = new HCallStub(CodeStub::TranscendentalCache, argument_count); - instr->set_transcendental_type(TranscendentalCache::COS); - PushAndAdd(instr, RelocInfo::kNoPosition); + result->set_transcendental_type(TranscendentalCache::COS); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateMathLog(int argument_count) { +void HGraphBuilder::GenerateMathLog(int argument_count, int ast_id) { ASSERT_EQ(1, argument_count); PushArgumentsForStubCall(argument_count); - HCallStub* instr = + HCallStub* result = new HCallStub(CodeStub::TranscendentalCache, argument_count); - instr->set_transcendental_type(TranscendentalCache::LOG); - PushAndAdd(instr, RelocInfo::kNoPosition); + result->set_transcendental_type(TranscendentalCache::LOG); + ast_context()->ReturnInstruction(result, ast_id); } -void HGraphBuilder::GenerateMathSqrt(int argument_count) { +void HGraphBuilder::GenerateMathSqrt(int argument_count, int ast_id) { BAILOUT("inlined runtime function: MathSqrt"); } // Check whether two RegExps are equivalent -void HGraphBuilder::GenerateIsRegExpEquivalent(int argument_count) { +void HGraphBuilder::GenerateIsRegExpEquivalent(int argument_count, + int ast_id) { BAILOUT("inlined runtime function: IsRegExpEquivalent"); } -void HGraphBuilder::GenerateGetCachedArrayIndex(int argument_count) { +void HGraphBuilder::GenerateGetCachedArrayIndex(int argument_count, + int ast_id) { BAILOUT("inlined runtime function: GetCachedArrayIndex"); } -void HGraphBuilder::GenerateFastAsciiArrayJoin(int argument_count) { +void HGraphBuilder::GenerateFastAsciiArrayJoin(int argument_count, + int ast_id) { BAILOUT("inlined runtime function: FastAsciiArrayJoin"); } diff --git a/src/hydrogen.h b/src/hydrogen.h index 91f3c9e..5611d8d 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -557,10 +557,29 @@ class AstContext { bool IsValue() const { return kind_ == Expression::kValue; } bool IsTest() const { return kind_ == Expression::kTest; } + // 'Fill' this context with a hydrogen value. The value is assumed to + // have already been inserted in the instruction stream (or not need to + // be, e.g., HPhi). Call this function in tail position in the Visit + // functions for expressions. + virtual void ReturnValue(HValue* value) = 0; + + // Add a hydrogen instruction to the instruction stream (recording an + // environment simulation if necessary) and then fill this context with + // the instruction as value. + virtual void ReturnInstruction(HInstruction* instr, int ast_id) = 0; + protected: AstContext(HGraphBuilder* owner, Expression::Context kind); virtual ~AstContext(); + HGraphBuilder* owner() const { return owner_; } + + // We want to be able to assert, in a context-specific way, that the stack + // height makes sense when the context is filled. +#ifdef DEBUG + int original_count_; +#endif + private: HGraphBuilder* owner_; Expression::Context kind_; @@ -573,6 +592,10 @@ class EffectContext: public AstContext { explicit EffectContext(HGraphBuilder* owner) : AstContext(owner, Expression::kEffect) { } + virtual ~EffectContext(); + + virtual void ReturnValue(HValue* value); + virtual void ReturnInstruction(HInstruction* instr, int ast_id); }; @@ -581,6 +604,10 @@ class ValueContext: public AstContext { explicit ValueContext(HGraphBuilder* owner) : AstContext(owner, Expression::kValue) { } + virtual ~ValueContext(); + + virtual void ReturnValue(HValue* value); + virtual void ReturnInstruction(HInstruction* instr, int ast_id); }; @@ -598,6 +625,9 @@ class TestContext: public AstContext { invert_false_(invert_false) { } + virtual void ReturnValue(HValue* value); + virtual void ReturnInstruction(HInstruction* instr, int ast_id); + static TestContext* cast(AstContext* context) { ASSERT(context->IsTest()); return reinterpret_cast(context); @@ -610,6 +640,10 @@ class TestContext: public AstContext { bool invert_false() { return invert_false_; } private: + // Build the shared core part of the translation unpacking a value into + // control flow. + void BuildBranch(HValue* value); + HBasicBlock* if_true_; HBasicBlock* if_false_; bool invert_true_; @@ -631,9 +665,25 @@ class HGraphBuilder: public AstVisitor { HGraph* CreateGraph(CompilationInfo* info); + // Simple accessors. + HGraph* graph() const { return graph_; } + HSubgraph* subgraph() const { return current_subgraph_; } + + HEnvironment* environment() const { return subgraph()->environment(); } + HBasicBlock* CurrentBlock() const { return subgraph()->exit_block(); } + + // Adding instructions. + HInstruction* AddInstruction(HInstruction* instr); + void AddSimulate(int id); + + // Bailout environment manipulation. + void Push(HValue* value) { environment()->Push(value); } + HValue* Pop() { return environment()->Pop(); } + private: // Type of a member function that generates inline code for a native function. - typedef void (HGraphBuilder::*InlineFunctionGenerator)(int argument_count); + typedef void (HGraphBuilder::*InlineFunctionGenerator)(int argument_count, + int ast_id); // Forward declarations for inner scope classes. class SubgraphScope; @@ -650,19 +700,14 @@ class HGraphBuilder: public AstVisitor { // Simple accessors. TypeFeedbackOracle* oracle() const { return oracle_; } - HGraph* graph() const { return graph_; } - HSubgraph* subgraph() const { return current_subgraph_; } AstContext* ast_context() const { return ast_context_; } void set_ast_context(AstContext* context) { ast_context_ = context; } AstContext* call_context() const { return call_context_; } HBasicBlock* function_return() const { return function_return_; } - HEnvironment* environment() const { return subgraph()->environment(); } - - HBasicBlock* CurrentBlock() const { return subgraph()->exit_block(); } // Generators for inline runtime functions. -#define INLINE_FUNCTION_GENERATOR_DECLARATION(Name, argc, ressize) \ - void Generate##Name(int argument_count); +#define INLINE_FUNCTION_GENERATOR_DECLARATION(Name, argc, ressize) \ + void Generate##Name(int argument_count, int ast_id); INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION) INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION) @@ -683,8 +728,6 @@ class HGraphBuilder: public AstVisitor { HSubgraph* true_graph, HSubgraph* false_graph); - void Push(HValue* value) { environment()->Push(value); } - HValue* Pop() { return environment()->Pop(); } HValue* Top() const { return environment()->Top(); } void Drop(int n) { environment()->Drop(n); } void Bind(Variable* var, HValue* value) { environment()->Bind(var, value); } @@ -708,18 +751,15 @@ class HGraphBuilder: public AstVisitor { HValue* VisitArgument(Expression* expr); void VisitArgumentList(ZoneList* arguments); - HInstruction* AddInstruction(HInstruction* instr); - void AddSimulate(int id); void AddPhi(HPhi* phi); void PushAndAdd(HInstruction* instr); - void PushAndAdd(HInstruction* instr, int position); void PushArgumentsForStubCall(int argument_count); - // Initialize the arguments to the call based on then environment, add it - // to the graph, and drop the arguments from the environment. - void ProcessCall(HCall* call, int source_position); + // Remove the arguments from the bailout environment and emit instructions + // to push them as outgoing parameters. + void ProcessCall(HCall* call); void AssumeRepresentation(HValue* value, Representation r); static Representation ToRepresentation(TypeInfo info); @@ -743,7 +783,7 @@ class HGraphBuilder: public AstVisitor { FunctionLiteral* function); // Helpers for flow graph construction. - void LookupGlobalPropertyCell(VariableProxy* expr, + void LookupGlobalPropertyCell(Variable* var, LookupResult* lookup, bool is_store); @@ -753,10 +793,11 @@ class HGraphBuilder: public AstVisitor { bool TryMathFunctionInline(Call* expr); void TraceInline(Handle target, bool result); - void HandleGlobalVariableAssignment(VariableProxy* proxy, + void HandleGlobalVariableAssignment(Variable* var, HValue* value, - int position); - void HandleGlobalVariableLoad(VariableProxy* expr); + int position, + int ast_id); + void HandlePropertyAssignment(Assignment* expr); void HandleCompoundAssignment(Assignment* expr); void HandlePolymorphicLoadNamedField(Property* expr, diff --git a/test/sputnik/README b/test/sputnik/README index 3d39a67..94c689b 100644 --- a/test/sputnik/README +++ b/test/sputnik/README @@ -1,6 +1,6 @@ To run the sputniktests you must check out the test suite from googlecode.com. The test expectations are currently relative to version 28. To get the tests run the following command within -v8/tests/sputnik/ +v8/test/sputnik/ svn co http://sputniktests.googlecode.com/svn/trunk/ -r28 sputniktests