| Index: src/hydrogen.cc
|
| ===================================================================
|
| --- src/hydrogen.cc (revision 6095)
|
| +++ src/hydrogen.cc (working copy)
|
| @@ -64,9 +64,7 @@
|
| first_instruction_index_(-1),
|
| last_instruction_index_(-1),
|
| deleted_phis_(4),
|
| - is_inline_return_target_(false),
|
| - inverted_(false),
|
| - deopt_predecessor_(NULL) {
|
| + is_inline_return_target_(false) {
|
| }
|
|
|
|
|
| @@ -1031,12 +1029,12 @@
|
| } else if (op == Token::LT || op == Token::LTE) {
|
| new_range = range->CopyClearLower();
|
| if (op == Token::LT) {
|
| - new_range->Add(-1);
|
| + new_range->AddConstant(-1);
|
| }
|
| } else if (op == Token::GT || op == Token::GTE) {
|
| new_range = range->CopyClearUpper();
|
| if (op == Token::GT) {
|
| - new_range->Add(1);
|
| + new_range->AddConstant(1);
|
| }
|
| }
|
|
|
| @@ -1292,15 +1290,15 @@
|
| for (int i = 0; i < graph_->blocks()->length(); i++) {
|
| HBasicBlock* block = graph_->blocks()->at(i);
|
| if (block->IsLoopHeader()) {
|
| - HBasicBlock* backedge = block->loop_information()->GetLastBackEdge();
|
| - HBasicBlock* dominator = backedge;
|
| - bool backedge_dominated_by_call = false;
|
| - while (dominator != block && !backedge_dominated_by_call) {
|
| + HBasicBlock* back_edge = block->loop_information()->GetLastBackEdge();
|
| + HBasicBlock* dominator = back_edge;
|
| + bool back_edge_dominated_by_call = false;
|
| + while (dominator != block && !back_edge_dominated_by_call) {
|
| HInstruction* instr = dominator->first();
|
| - while (instr != NULL && !backedge_dominated_by_call) {
|
| + while (instr != NULL && !back_edge_dominated_by_call) {
|
| if (instr->IsCall()) {
|
| - RemoveStackCheck(backedge);
|
| - backedge_dominated_by_call = true;
|
| + RemoveStackCheck(back_edge);
|
| + back_edge_dominated_by_call = true;
|
| }
|
| instr = instr->next();
|
| }
|
| @@ -2053,38 +2051,29 @@
|
|
|
|
|
| void TestContext::BuildBranch(HValue* value) {
|
| + // We expect the graph to be in edge-split form: there is no edge that
|
| + // connects a branch node to a join node. We conservatively ensure that
|
| + // property by always adding an empty block on the outgoing edges of this
|
| + // branch.
|
| HGraphBuilder* builder = owner();
|
| - HBasicBlock* materialize_true = builder->graph()->CreateBasicBlock();
|
| - HBasicBlock* materialize_false = builder->graph()->CreateBasicBlock();
|
| - HBranch* branch = new HBranch(materialize_true, materialize_false, value);
|
| + HBasicBlock* empty_true = builder->graph()->CreateBasicBlock();
|
| + HBasicBlock* empty_false = builder->graph()->CreateBasicBlock();
|
| + HBranch* branch = new HBranch(empty_true, empty_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);
|
| + HValue* const no_return_value = NULL;
|
| + HBasicBlock* true_target = if_true();
|
| + if (true_target->IsInlineReturnTarget()) {
|
| + empty_true->AddLeaveInlined(no_return_value, true_target);
|
| } else {
|
| - materialize_true->last_environment()->Push(true_value);
|
| - materialize_true->Goto(true_block);
|
| + empty_true->Goto(true_target);
|
| }
|
|
|
| - 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);
|
| + HBasicBlock* false_target = if_false();
|
| + if (false_target->IsInlineReturnTarget()) {
|
| + empty_false->AddLeaveInlined(no_return_value, false_target);
|
| } else {
|
| - materialize_false->last_environment()->Push(false_value);
|
| - materialize_false->Goto(false_block);
|
| + empty_false->Goto(false_target);
|
| }
|
| builder->subgraph()->set_exit_block(NULL);
|
| }
|
| @@ -2118,6 +2107,13 @@
|
| } while (false)
|
|
|
|
|
| +#define VISIT_FOR_CONTROL(expr, true_block, false_block) \
|
| + do { \
|
| + VisitForControl(expr, true_block, false_block); \
|
| + if (HasStackOverflow()) return; \
|
| + } while (false)
|
| +
|
| +
|
| // 'thing' could be an expression, statement, or list of statements.
|
| #define ADD_TO_SUBGRAPH(graph, thing) \
|
| do { \
|
| @@ -2170,6 +2166,14 @@
|
| }
|
|
|
|
|
| +void HGraphBuilder::VisitForControl(Expression* expr,
|
| + HBasicBlock* true_block,
|
| + HBasicBlock* false_block) {
|
| + TestContext for_test(this, true_block, false_block);
|
| + Visit(expr);
|
| +}
|
| +
|
| +
|
| HValue* HGraphBuilder::VisitArgument(Expression* expr) {
|
| VisitForValue(expr);
|
| if (HasStackOverflow() || !subgraph()->HasExit()) return NULL;
|
| @@ -2259,52 +2263,6 @@
|
| }
|
|
|
|
|
| -void HGraphBuilder::VisitCondition(Expression* expr,
|
| - HBasicBlock* true_block,
|
| - HBasicBlock* false_block,
|
| - bool invert_true,
|
| - bool invert_false) {
|
| - VisitForControl(expr, true_block, false_block, invert_true, invert_false);
|
| - CHECK_BAILOUT;
|
| -#ifdef DEBUG
|
| - HValue* value = true_block->predecessors()->at(0)->last_environment()->Top();
|
| - true_block->set_cond(HConstant::cast(value)->handle());
|
| -
|
| - value = false_block->predecessors()->at(0)->last_environment()->Top();
|
| - false_block->set_cond(HConstant::cast(value)->handle());
|
| -#endif
|
| -
|
| - true_block->SetJoinId(expr->id());
|
| - false_block->SetJoinId(expr->id());
|
| - true_block->last_environment()->Pop();
|
| - false_block->last_environment()->Pop();
|
| -}
|
| -
|
| -
|
| -void HGraphBuilder::AddConditionToSubgraph(HSubgraph* subgraph,
|
| - Expression* expr,
|
| - HSubgraph* true_graph,
|
| - HSubgraph* false_graph) {
|
| - SubgraphScope scope(this, subgraph);
|
| - VisitCondition(expr,
|
| - true_graph->entry_block(),
|
| - false_graph->entry_block(),
|
| - false,
|
| - false);
|
| -}
|
| -
|
| -
|
| -void HGraphBuilder::VisitForControl(Expression* expr,
|
| - HBasicBlock* true_block,
|
| - HBasicBlock* false_block,
|
| - bool invert_true,
|
| - bool invert_false) {
|
| - TestContext for_test(this, true_block, false_block,
|
| - invert_true, invert_false);
|
| - Visit(expr);
|
| -}
|
| -
|
| -
|
| void HGraphBuilder::AddToSubgraph(HSubgraph* graph,
|
| ZoneList<Statement*>* stmts) {
|
| SubgraphScope scope(this, graph);
|
| @@ -2484,19 +2442,24 @@
|
|
|
| void HGraphBuilder::VisitIfStatement(IfStatement* stmt) {
|
| if (stmt->condition()->ToBooleanIsTrue()) {
|
| + AddSimulate(stmt->ThenId());
|
| Visit(stmt->then_statement());
|
| } else if (stmt->condition()->ToBooleanIsFalse()) {
|
| + AddSimulate(stmt->ElseId());
|
| Visit(stmt->else_statement());
|
| } else {
|
| HSubgraph* then_graph = CreateEmptySubgraph();
|
| HSubgraph* else_graph = CreateEmptySubgraph();
|
| - VisitCondition(stmt->condition(),
|
| - then_graph->entry_block(),
|
| - else_graph->entry_block(),
|
| - false, false);
|
| - if (HasStackOverflow()) return;
|
| + VISIT_FOR_CONTROL(stmt->condition(),
|
| + then_graph->entry_block(),
|
| + else_graph->entry_block());
|
| +
|
| + then_graph->entry_block()->SetJoinId(stmt->ThenId());
|
| ADD_TO_SUBGRAPH(then_graph, stmt->then_statement());
|
| +
|
| + else_graph->entry_block()->SetJoinId(stmt->ElseId());
|
| ADD_TO_SUBGRAPH(else_graph, stmt->else_statement());
|
| +
|
| current_subgraph_->AppendJoin(then_graph, else_graph, stmt);
|
| }
|
| }
|
| @@ -2526,9 +2489,7 @@
|
| TestContext* test = TestContext::cast(context);
|
| VisitForControl(stmt->expression(),
|
| test->if_true(),
|
| - test->if_false(),
|
| - false,
|
| - false);
|
| + test->if_false());
|
| } else {
|
| HValue* return_value = NULL;
|
| if (context->IsEffect()) {
|
| @@ -2574,6 +2535,8 @@
|
|
|
| void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
| VISIT_FOR_VALUE(stmt->tag());
|
| + // TODO(3168478): simulate added for tag should be enough.
|
| + AddSimulate(stmt->EntryId());
|
| HValue* switch_value = Pop();
|
|
|
| ZoneList<CaseClause*>* clauses = stmt->cases();
|
| @@ -2581,11 +2544,17 @@
|
| if (num_clauses == 0) return;
|
| if (num_clauses > 128) BAILOUT("SwitchStatement: too many clauses");
|
|
|
| + int num_smi_clauses = num_clauses;
|
| for (int i = 0; i < num_clauses; i++) {
|
| CaseClause* clause = clauses->at(i);
|
| if (clause->is_default()) continue;
|
| clause->RecordTypeFeedback(oracle());
|
| - if (!clause->IsSmiCompare()) BAILOUT("SwitchStatement: non-smi compare");
|
| + if (!clause->IsSmiCompare()) {
|
| + if (i == 0) BAILOUT("SwitchStatement: no smi compares");
|
| + // We will deoptimize if the first non-smi compare is reached.
|
| + num_smi_clauses = i;
|
| + break;
|
| + }
|
| if (!clause->label()->IsSmiLiteral()) {
|
| BAILOUT("SwitchStatement: non-literal switch label");
|
| }
|
| @@ -2596,17 +2565,18 @@
|
|
|
| // Build a series of empty subgraphs for the comparisons.
|
| // The default clause does not have a comparison subgraph.
|
| - ZoneList<HSubgraph*> compare_graphs(num_clauses);
|
| - for (int i = 0; i < num_clauses; i++) {
|
| - HSubgraph* subgraph = !clauses->at(i)->is_default()
|
| - ? CreateEmptySubgraph()
|
| - : NULL;
|
| - compare_graphs.Add(subgraph);
|
| + ZoneList<HSubgraph*> compare_graphs(num_smi_clauses);
|
| + for (int i = 0; i < num_smi_clauses; i++) {
|
| + if (clauses->at(i)->is_default()) {
|
| + compare_graphs.Add(NULL);
|
| + } else {
|
| + compare_graphs.Add(CreateEmptySubgraph());
|
| + }
|
| }
|
|
|
| HSubgraph* prev_graph = current_subgraph_;
|
| HCompare* prev_compare_inst = NULL;
|
| - for (int i = 0; i < num_clauses; i++) {
|
| + for (int i = 0; i < num_smi_clauses; i++) {
|
| CaseClause* clause = clauses->at(i);
|
| if (clause->is_default()) continue;
|
|
|
| @@ -2623,6 +2593,7 @@
|
| }
|
|
|
| // Build instructions for current subgraph.
|
| + ASSERT(clause->IsSmiCompare());
|
| prev_compare_inst = BuildSwitchCompare(subgraph, switch_value, clause);
|
| if (HasStackOverflow()) return;
|
|
|
| @@ -2642,33 +2613,52 @@
|
| prev_compare_inst));
|
| }
|
|
|
| + // If we have a non-smi compare clause, we deoptimize after trying
|
| + // all the previous compares.
|
| + if (num_smi_clauses < num_clauses) {
|
| + last_false_block->Finish(new HDeoptimize);
|
| + }
|
| +
|
| // Build statement blocks, connect them to their comparison block and
|
| // to the previous statement block, if there is a fall-through.
|
| HSubgraph* previous_subgraph = NULL;
|
| for (int i = 0; i < num_clauses; i++) {
|
| CaseClause* clause = clauses->at(i);
|
| - HSubgraph* subgraph = CreateEmptySubgraph();
|
| + // Subgraph for the statements of the clause is only created when
|
| + // it's reachable either from the corresponding compare or as a
|
| + // fall-through from previous statements.
|
| + HSubgraph* subgraph = NULL;
|
|
|
| - if (clause->is_default()) {
|
| - // Default clause: Connect it to the last false block.
|
| - last_false_block->Finish(new HGoto(subgraph->entry_block()));
|
| - } else {
|
| - // Connect with the corresponding comparison.
|
| - HBasicBlock* empty =
|
| - compare_graphs.at(i)->exit_block()->end()->FirstSuccessor();
|
| - empty->Finish(new HGoto(subgraph->entry_block()));
|
| + if (i < num_smi_clauses) {
|
| + if (clause->is_default()) {
|
| + if (!last_false_block->IsFinished()) {
|
| + // Default clause: Connect it to the last false block.
|
| + subgraph = CreateEmptySubgraph();
|
| + last_false_block->Finish(new HGoto(subgraph->entry_block()));
|
| + }
|
| + } else {
|
| + ASSERT(clause->IsSmiCompare());
|
| + // Connect with the corresponding comparison.
|
| + subgraph = CreateEmptySubgraph();
|
| + HBasicBlock* empty =
|
| + compare_graphs.at(i)->exit_block()->end()->FirstSuccessor();
|
| + empty->Finish(new HGoto(subgraph->entry_block()));
|
| + }
|
| }
|
|
|
| // Check for fall-through from previous statement block.
|
| if (previous_subgraph != NULL && previous_subgraph->HasExit()) {
|
| + if (subgraph == NULL) subgraph = CreateEmptySubgraph();
|
| previous_subgraph->exit_block()->
|
| Finish(new HGoto(subgraph->entry_block()));
|
| }
|
|
|
| - ADD_TO_SUBGRAPH(subgraph, clause->statements());
|
| - HBasicBlock* break_block = subgraph->BundleBreak(stmt);
|
| - if (break_block != NULL) {
|
| - break_block->Finish(new HGoto(single_exit_block));
|
| + if (subgraph != NULL) {
|
| + ADD_TO_SUBGRAPH(subgraph, clause->statements());
|
| + HBasicBlock* break_block = subgraph->BundleBreak(stmt);
|
| + if (break_block != NULL) {
|
| + break_block->Finish(new HGoto(single_exit_block));
|
| + }
|
| }
|
|
|
| previous_subgraph = subgraph;
|
| @@ -2676,7 +2666,7 @@
|
|
|
| // If the last statement block has a fall-through, connect it to the
|
| // single exit block.
|
| - if (previous_subgraph->HasExit()) {
|
| + if (previous_subgraph != NULL && previous_subgraph->HasExit()) {
|
| previous_subgraph->exit_block()->Finish(new HGoto(single_exit_block));
|
| }
|
|
|
| @@ -2742,8 +2732,14 @@
|
| } else {
|
| HSubgraph* go_back = CreateEmptySubgraph();
|
| HSubgraph* exit = CreateEmptySubgraph();
|
| - AddConditionToSubgraph(body_graph, stmt->cond(), go_back, exit);
|
| - if (HasStackOverflow()) return;
|
| + {
|
| + SubgraphScope scope(this, body_graph);
|
| + VISIT_FOR_CONTROL(stmt->cond(),
|
| + go_back->entry_block(),
|
| + exit->entry_block());
|
| + go_back->entry_block()->SetJoinId(stmt->BackEdgeId());
|
| + exit->entry_block()->SetJoinId(stmt->ExitId());
|
| + }
|
| current_subgraph_->AppendDoWhile(body_graph, stmt, go_back, exit);
|
| }
|
| }
|
| @@ -2770,8 +2766,14 @@
|
| cond_graph = CreateLoopHeaderSubgraph(environment());
|
| body_graph = CreateEmptySubgraph();
|
| exit_graph = CreateEmptySubgraph();
|
| - AddConditionToSubgraph(cond_graph, stmt->cond(), body_graph, exit_graph);
|
| - if (HasStackOverflow()) return;
|
| + {
|
| + SubgraphScope scope(this, cond_graph);
|
| + VISIT_FOR_CONTROL(stmt->cond(),
|
| + body_graph->entry_block(),
|
| + exit_graph->entry_block());
|
| + body_graph->entry_block()->SetJoinId(stmt->BodyId());
|
| + exit_graph->entry_block()->SetJoinId(stmt->ExitId());
|
| + }
|
| ADD_TO_SUBGRAPH(body_graph, stmt->body());
|
| }
|
|
|
| @@ -2821,13 +2823,18 @@
|
| cond_graph = CreateLoopHeaderSubgraph(environment());
|
| body_graph = CreateEmptySubgraph();
|
| exit_graph = CreateEmptySubgraph();
|
| - AddConditionToSubgraph(cond_graph, stmt->cond(), body_graph, exit_graph);
|
| - if (HasStackOverflow()) return;
|
| - ADD_TO_SUBGRAPH(body_graph, stmt->body());
|
| + {
|
| + SubgraphScope scope(this, cond_graph);
|
| + VISIT_FOR_CONTROL(stmt->cond(),
|
| + body_graph->entry_block(),
|
| + exit_graph->entry_block());
|
| + body_graph->entry_block()->SetJoinId(stmt->BodyId());
|
| + exit_graph->entry_block()->SetJoinId(stmt->ExitId());
|
| + }
|
| } else {
|
| body_graph = CreateLoopHeaderSubgraph(environment());
|
| - ADD_TO_SUBGRAPH(body_graph, stmt->body());
|
| }
|
| + ADD_TO_SUBGRAPH(body_graph, stmt->body());
|
|
|
| HSubgraph* next_graph = NULL;
|
| body_graph->ResolveContinue(stmt);
|
| @@ -2886,13 +2893,16 @@
|
| void HGraphBuilder::VisitConditional(Conditional* expr) {
|
| HSubgraph* then_graph = CreateEmptySubgraph();
|
| HSubgraph* else_graph = CreateEmptySubgraph();
|
| - VisitCondition(expr->condition(),
|
| - then_graph->entry_block(),
|
| - else_graph->entry_block(),
|
| - false, false);
|
| - if (HasStackOverflow()) return;
|
| + VISIT_FOR_CONTROL(expr->condition(),
|
| + then_graph->entry_block(),
|
| + else_graph->entry_block());
|
| +
|
| + then_graph->entry_block()->SetJoinId(expr->ThenId());
|
| ADD_TO_SUBGRAPH(then_graph, expr->then_expression());
|
| +
|
| + else_graph->entry_block()->SetJoinId(expr->ElseId());
|
| ADD_TO_SUBGRAPH(else_graph, expr->else_expression());
|
| +
|
| current_subgraph_->AppendJoin(then_graph, else_graph, expr);
|
| ast_context()->ReturnValue(Pop());
|
| }
|
| @@ -3023,8 +3033,9 @@
|
| // 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));
|
|
|
| + HLoadElements* elements = NULL;
|
| +
|
| for (int i = 0; i < length; i++) {
|
| Expression* subexpr = subexprs->at(i);
|
| // If the subexpression is a literal or a simple materialized literal it
|
| @@ -3034,6 +3045,13 @@
|
| VISIT_FOR_VALUE(subexpr);
|
| HValue* value = Pop();
|
| if (!Smi::IsValid(i)) BAILOUT("Non-smi key in array literal");
|
| +
|
| + // Load the elements array before the first store.
|
| + if (elements == NULL) {
|
| + elements = new HLoadElements(literal);
|
| + AddInstruction(elements);
|
| + }
|
| +
|
| HValue* key = AddInstruction(new HConstant(Handle<Object>(Smi::FromInt(i)),
|
| Representation::Integer32()));
|
| AddInstruction(new HStoreKeyedFastElement(elements, key, value));
|
| @@ -3147,6 +3165,9 @@
|
| if (lookup->type() == MAP_TRANSITION) {
|
| Handle<Map> transition(lookup->GetTransitionMapFromMap(*type));
|
| instr->set_transition(transition);
|
| + // TODO(fschneider): Record the new map type of the object in the IR to
|
| + // enable elimination of redundant checks after the transition store.
|
| + instr->SetFlag(HValue::kChangesMaps);
|
| }
|
| return instr;
|
| }
|
| @@ -3239,7 +3260,7 @@
|
| }
|
|
|
| HBasicBlock* new_exit_block =
|
| - BuildTypeSwitch(&maps, &subgraphs, object, expr->id());
|
| + BuildTypeSwitch(&maps, &subgraphs, object, expr->AssignmentId());
|
| subgraph()->set_exit_block(new_exit_block);
|
| }
|
|
|
| @@ -3511,9 +3532,10 @@
|
| maps.Add(map);
|
| HSubgraph* subgraph = CreateBranchSubgraph(environment());
|
| SubgraphScope scope(this, subgraph);
|
| - HInstruction* instr =
|
| + HLoadNamedField* instr =
|
| BuildLoadNamedField(object, expr, map, &lookup, false);
|
| instr->set_position(expr->position());
|
| + instr->ClearFlag(HValue::kUseGVN); // Don't do GVN on polymorphic loads.
|
| PushAndAdd(instr);
|
| subgraphs.Add(subgraph);
|
| } else {
|
| @@ -3552,11 +3574,11 @@
|
| }
|
|
|
|
|
| -HInstruction* HGraphBuilder::BuildLoadNamedField(HValue* object,
|
| - Property* expr,
|
| - Handle<Map> type,
|
| - LookupResult* lookup,
|
| - bool smi_and_map_check) {
|
| +HLoadNamedField* HGraphBuilder::BuildLoadNamedField(HValue* object,
|
| + Property* expr,
|
| + Handle<Map> type,
|
| + LookupResult* lookup,
|
| + bool smi_and_map_check) {
|
| if (smi_and_map_check) {
|
| AddInstruction(new HCheckNonSmi(object));
|
| AddInstruction(new HCheckMap(object, type));
|
| @@ -3596,6 +3618,11 @@
|
| map,
|
| &lookup,
|
| true);
|
| + } else if (lookup.IsProperty() && lookup.type() == CONSTANT_FUNCTION) {
|
| + AddInstruction(new HCheckNonSmi(obj));
|
| + AddInstruction(new HCheckMap(obj, map));
|
| + Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*map));
|
| + return new HConstant(function, Representation::Tagged());
|
| } else {
|
| return BuildLoadNamedGeneric(obj, expr);
|
| }
|
| @@ -3942,10 +3969,7 @@
|
| if_true->MarkAsInlineReturnTarget();
|
| if_false->MarkAsInlineReturnTarget();
|
| // AstContext constructor pushes on the context stack.
|
| - bool invert_true = TestContext::cast(ast_context())->invert_true();
|
| - bool invert_false = TestContext::cast(ast_context())->invert_false();
|
| - test_context = new TestContext(this, if_true, if_false,
|
| - invert_true, invert_false);
|
| + test_context = new TestContext(this, if_true, if_false);
|
| function_return_ = NULL;
|
| } else {
|
| // Inlined body is treated as if it occurs in the original call context.
|
| @@ -3989,16 +4013,15 @@
|
| // simply jumping to the false target.
|
| //
|
| // TODO(3168478): refactor to avoid this.
|
| - HBasicBlock* materialize_true = graph()->CreateBasicBlock();
|
| - HBasicBlock* materialize_false = graph()->CreateBasicBlock();
|
| + HBasicBlock* empty_true = graph()->CreateBasicBlock();
|
| + HBasicBlock* empty_false = graph()->CreateBasicBlock();
|
| HBranch* branch =
|
| - new HBranch(materialize_true, materialize_false, return_value);
|
| + new HBranch(empty_true, empty_false, return_value);
|
| body->exit_block()->Finish(branch);
|
|
|
| - materialize_true->AddLeaveInlined(graph()->GetConstantTrue(),
|
| - test_context->if_true());
|
| - materialize_false->AddLeaveInlined(graph()->GetConstantFalse(),
|
| - test_context->if_false());
|
| + HValue* const no_return_value = NULL;
|
| + empty_true->AddLeaveInlined(no_return_value, test_context->if_true());
|
| + empty_false->AddLeaveInlined(no_return_value, test_context->if_false());
|
| }
|
| body->set_exit_block(NULL);
|
| }
|
| @@ -4017,35 +4040,20 @@
|
| if_false->SetJoinId(expr->id());
|
| ASSERT(ast_context() == test_context);
|
| delete test_context; // Destructor pops from expression context stack.
|
| +
|
| // Forward to the real test context.
|
| -
|
| - // Discard the lingering branch value (which may be true or false,
|
| - // depending on whether the final condition was negated) and jump to the
|
| - // true target with a true branch value.
|
| + HValue* const no_return_value = NULL;
|
| HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
|
| - bool invert_true = TestContext::cast(ast_context())->invert_true();
|
| - HValue* true_value = invert_true
|
| - ? graph()->GetConstantFalse()
|
| - : graph()->GetConstantTrue();
|
| - if_true->last_environment()->Pop();
|
| if (true_target->IsInlineReturnTarget()) {
|
| - if_true->AddLeaveInlined(true_value, true_target);
|
| + if_true->AddLeaveInlined(no_return_value, true_target);
|
| } else {
|
| - if_true->last_environment()->Push(true_value);
|
| if_true->Goto(true_target);
|
| }
|
|
|
| - // Do the same for the false target.
|
| HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
|
| - bool invert_false = TestContext::cast(ast_context())->invert_false();
|
| - HValue* false_value = invert_false
|
| - ? graph()->GetConstantTrue()
|
| - : graph()->GetConstantFalse();
|
| - if_false->last_environment()->Pop();
|
| if (false_target->IsInlineReturnTarget()) {
|
| - if_false->AddLeaveInlined(false_value, false_target);
|
| + if_false->AddLeaveInlined(no_return_value, false_target);
|
| } else {
|
| - if_false->last_environment()->Push(false_value);
|
| if_false->Goto(false_target);
|
| }
|
|
|
| @@ -4072,7 +4080,7 @@
|
| ASSERT(target->IsInlineReturnTarget());
|
| AddInstruction(new HLeaveInlined);
|
| HEnvironment* outer = last_environment()->outer();
|
| - outer->Push(return_value);
|
| + if (return_value != NULL) outer->Push(return_value);
|
| UpdateEnvironment(outer);
|
| Goto(target);
|
| }
|
| @@ -4080,13 +4088,17 @@
|
|
|
| bool HGraphBuilder::TryMathFunctionInline(Call* expr) {
|
| // Try to inline calls like Math.* as operations in the calling function.
|
| - MathFunctionId id = expr->target()->shared()->math_function_id();
|
| + if (!expr->target()->shared()->IsBuiltinMathFunction()) return false;
|
| + BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
|
| int argument_count = expr->arguments()->length() + 1; // Plus receiver.
|
| switch (id) {
|
| case kMathRound:
|
| case kMathFloor:
|
| case kMathAbs:
|
| case kMathSqrt:
|
| + case kMathLog:
|
| + case kMathSin:
|
| + case kMathCos:
|
| if (argument_count == 2) {
|
| HValue* argument = Pop();
|
| Drop(1); // Receiver.
|
| @@ -4142,7 +4154,7 @@
|
| }
|
| break;
|
| default:
|
| - // Either not a special math function or not yet supported for inlining.
|
| + // Not yet supported for inlining.
|
| break;
|
| }
|
| return false;
|
| @@ -4163,7 +4175,7 @@
|
| if (args->length() != 2) return false;
|
|
|
| VariableProxy* arg_two = args->at(1)->AsVariableProxy();
|
| - if (arg_two == NULL) return false;
|
| + if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return false;
|
| HValue* arg_two_value = environment()->Lookup(arg_two->var());
|
| if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
|
|
|
| @@ -4456,19 +4468,19 @@
|
| TestContext* context = TestContext::cast(ast_context());
|
| VisitForControl(expr->expression(),
|
| context->if_false(),
|
| - context->if_true(),
|
| - !context->invert_false(),
|
| - !context->invert_true());
|
| + context->if_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;
|
| + VISIT_FOR_CONTROL(expr->expression(),
|
| + false_graph->entry_block(),
|
| + true_graph->entry_block());
|
| + true_graph->entry_block()->SetJoinId(expr->expression()->id());
|
| true_graph->environment()->Push(graph_->GetConstantTrue());
|
| +
|
| + false_graph->entry_block()->SetJoinId(expr->expression()->id());
|
| false_graph->environment()->Push(graph_->GetConstantFalse());
|
| +
|
| current_subgraph_->AppendJoin(true_graph, false_graph, expr);
|
| ast_context()->ReturnValue(Pop());
|
| }
|
| @@ -4531,26 +4543,25 @@
|
|
|
| VISIT_FOR_VALUE(target);
|
|
|
| - HValue* value = Pop();
|
| - HInstruction* instr = BuildIncrement(value, inc);
|
| - AddInstruction(instr);
|
| + // Match the full code generator stack by simulating an extra stack
|
| + // element for postfix operations in a non-effect context.
|
| + bool has_extra = expr->is_postfix() && !ast_context()->IsEffect();
|
| + HValue* before = has_extra ? Top() : Pop();
|
| + HInstruction* after = BuildIncrement(before, inc);
|
| + AddInstruction(after);
|
| + Push(after);
|
|
|
| - if (expr->is_prefix()) {
|
| - Push(instr);
|
| - } else {
|
| - Push(value);
|
| - }
|
| -
|
| if (var->is_global()) {
|
| HandleGlobalVariableAssignment(var,
|
| - instr,
|
| + after,
|
| expr->position(),
|
| expr->AssignmentId());
|
| } else {
|
| ASSERT(var->IsStackAllocated());
|
| - Bind(var, instr);
|
| + Bind(var, after);
|
| }
|
| - ast_context()->ReturnValue(Pop());
|
| + Drop(has_extra ? 2 : 1);
|
| + ast_context()->ReturnValue(expr->is_postfix() ? before : after);
|
|
|
| } else if (prop != NULL) {
|
| prop->RecordTypeFeedback(oracle());
|
| @@ -4559,7 +4570,7 @@
|
| // Named property.
|
|
|
| // Match the full code generator stack by simulating an extra stack
|
| - // element for postfix operations in a value context.
|
| + // element for postfix operations in a non-effect context.
|
| bool has_extra = expr->is_postfix() && !ast_context()->IsEffect();
|
| if (has_extra) Push(graph_->GetConstantUndefined());
|
|
|
| @@ -4600,7 +4611,7 @@
|
| // Keyed property.
|
|
|
| // Match the full code generator stack by simulate an extra stack element
|
| - // for postfix operations in a value context.
|
| + // for postfix operations in a non-effect context.
|
| bool has_extra = expr->is_postfix() && !ast_context()->IsEffect();
|
| if (has_extra) Push(graph_->GetConstantUndefined());
|
|
|
| @@ -4733,18 +4744,14 @@
|
| // Translate left subexpression.
|
| HBasicBlock* eval_right = graph()->CreateBasicBlock();
|
| if (is_logical_and) {
|
| - VisitForControl(expr->left(), eval_right, context->if_false(),
|
| - false, context->invert_false());
|
| + VISIT_FOR_CONTROL(expr->left(), eval_right, context->if_false());
|
| } else {
|
| - VisitForControl(expr->left(), context->if_true(), eval_right,
|
| - context->invert_true(), false);
|
| + VISIT_FOR_CONTROL(expr->left(), context->if_true(), eval_right);
|
| }
|
| - if (HasStackOverflow()) return;
|
| - eval_right->SetJoinId(expr->left()->id());
|
| + eval_right->SetJoinId(expr->RightId());
|
|
|
| // 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());
|
|
|
| @@ -4949,17 +4956,21 @@
|
| }
|
|
|
|
|
| +void HGraphBuilder::GenerateIsObject(int argument_count, int ast_id) {
|
| + ASSERT(argument_count == 1);
|
| +
|
| + HValue* value = Pop();
|
| + HIsObject* test = new HIsObject(value);
|
| + ast_context()->ReturnInstruction(test, ast_id);
|
| +}
|
| +
|
| +
|
| void HGraphBuilder::GenerateIsNonNegativeSmi(int argument_count,
|
| int ast_id) {
|
| BAILOUT("inlined runtime function: IsNonNegativeSmi");
|
| }
|
|
|
|
|
| -void HGraphBuilder::GenerateIsObject(int argument_count, int ast_id) {
|
| - BAILOUT("inlined runtime function: IsObject");
|
| -}
|
| -
|
| -
|
| void HGraphBuilder::GenerateIsUndetectableObject(int argument_count,
|
| int ast_id) {
|
| BAILOUT("inlined runtime function: IsUndetectableObject");
|
|
|