diff options
Diffstat (limited to 'deps/v8/src/hydrogen.cc')
-rw-r--r-- | deps/v8/src/hydrogen.cc | 1000 |
1 files changed, 703 insertions, 297 deletions
diff --git a/deps/v8/src/hydrogen.cc b/deps/v8/src/hydrogen.cc index c625fba8d..5c0703bc3 100644 --- a/deps/v8/src/hydrogen.cc +++ b/deps/v8/src/hydrogen.cc @@ -164,10 +164,11 @@ void HBasicBlock::Finish(HControlInstruction* end) { } -void HBasicBlock::Goto(HBasicBlock* block) { +void HBasicBlock::Goto(HBasicBlock* block, bool drop_extra) { if (block->IsInlineReturnTarget()) { AddInstruction(new(zone()) HLeaveInlined); last_environment_ = last_environment()->outer(); + if (drop_extra) last_environment_->Drop(1); } AddSimulate(AstNode::kNoNumber); HGoto* instr = new(zone()) HGoto(block); @@ -175,11 +176,14 @@ void HBasicBlock::Goto(HBasicBlock* block) { } -void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) { +void HBasicBlock::AddLeaveInlined(HValue* return_value, + HBasicBlock* target, + bool drop_extra) { ASSERT(target->IsInlineReturnTarget()); ASSERT(return_value != NULL); AddInstruction(new(zone()) HLeaveInlined); last_environment_ = last_environment()->outer(); + if (drop_extra) last_environment_->Drop(1); last_environment()->Push(return_value); AddSimulate(AstNode::kNoNumber); HGoto* instr = new(zone()) HGoto(target); @@ -422,7 +426,7 @@ class ReachabilityAnalyzer BASE_EMBEDDED { }; -void HGraph::Verify() const { +void HGraph::Verify(bool do_full_verify) const { for (int i = 0; i < blocks_.length(); i++) { HBasicBlock* block = blocks_.at(i); @@ -473,25 +477,27 @@ void HGraph::Verify() const { // Check special property of first block to have no predecessors. ASSERT(blocks_.at(0)->predecessors()->is_empty()); - // Check that the graph is fully connected. - ReachabilityAnalyzer analyzer(entry_block_, blocks_.length(), NULL); - ASSERT(analyzer.visited_count() == blocks_.length()); + if (do_full_verify) { + // Check that the graph is fully connected. + ReachabilityAnalyzer analyzer(entry_block_, blocks_.length(), NULL); + ASSERT(analyzer.visited_count() == blocks_.length()); - // Check that entry block dominator is NULL. - ASSERT(entry_block_->dominator() == NULL); + // Check that entry block dominator is NULL. + ASSERT(entry_block_->dominator() == NULL); - // Check dominators. - for (int i = 0; i < blocks_.length(); ++i) { - HBasicBlock* block = blocks_.at(i); - if (block->dominator() == NULL) { - // Only start block may have no dominator assigned to. - ASSERT(i == 0); - } else { - // Assert that block is unreachable if dominator must not be visited. - ReachabilityAnalyzer dominator_analyzer(entry_block_, - blocks_.length(), - block->dominator()); - ASSERT(!dominator_analyzer.reachable()->Contains(block->block_id())); + // Check dominators. + for (int i = 0; i < blocks_.length(); ++i) { + HBasicBlock* block = blocks_.at(i); + if (block->dominator() == NULL) { + // Only start block may have no dominator assigned to. + ASSERT(i == 0); + } else { + // Assert that block is unreachable if dominator must not be visited. + ReachabilityAnalyzer dominator_analyzer(entry_block_, + blocks_.length(), + block->dominator()); + ASSERT(!dominator_analyzer.reachable()->Contains(block->block_id())); + } } } } @@ -539,7 +545,7 @@ HConstant* HGraph::GetConstantHole() { HGraphBuilder::HGraphBuilder(CompilationInfo* info, TypeFeedbackOracle* oracle) : function_state_(NULL), - initial_function_state_(this, info, oracle), + initial_function_state_(this, info, oracle, false), ast_context_(NULL), break_scope_(NULL), graph_(NULL), @@ -728,6 +734,7 @@ void HGraph::Postorder(HBasicBlock* block, Postorder(it.Current(), visited, order, block); } } else { + ASSERT(block->IsFinished()); for (HSuccessorIterator it(block->end()); !it.Done(); it.Advance()) { Postorder(it.Current(), visited, order, loop_header); } @@ -750,7 +757,7 @@ void HGraph::AssignDominators() { // All others are back edges, and thus cannot dominate the loop header. blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->first()); } else { - for (int j = 0; j < blocks_[i]->predecessors()->length(); ++j) { + for (int j = blocks_[i]->predecessors()->length() - 1; j >= 0; --j) { blocks_[i]->AssignCommonDominator(blocks_[i]->predecessors()->at(j)); } } @@ -850,7 +857,7 @@ void HGraph::EliminateUnreachablePhis() { } -bool HGraph::CheckPhis() { +bool HGraph::CheckArgumentsPhiUses() { int block_count = blocks_.length(); for (int i = 0; i < block_count; ++i) { for (int j = 0; j < blocks_[i]->phis()->length(); ++j) { @@ -863,13 +870,11 @@ bool HGraph::CheckPhis() { } -bool HGraph::CollectPhis() { +bool HGraph::CheckConstPhiUses() { int block_count = blocks_.length(); - phi_list_ = new ZoneList<HPhi*>(block_count); for (int i = 0; i < block_count; ++i) { for (int j = 0; j < blocks_[i]->phis()->length(); ++j) { HPhi* phi = blocks_[i]->phis()->at(j); - phi_list_->Add(phi); // Check for the hole value (from an uninitialized const). for (int k = 0; k < phi->OperandCount(); k++) { if (phi->OperandAt(k) == GetConstantHole()) return false; @@ -880,6 +885,18 @@ bool HGraph::CollectPhis() { } +void HGraph::CollectPhis() { + int block_count = blocks_.length(); + phi_list_ = new ZoneList<HPhi*>(block_count); + for (int i = 0; i < block_count; ++i) { + for (int j = 0; j < blocks_[i]->phis()->length(); ++j) { + HPhi* phi = blocks_[i]->phis()->at(j); + phi_list_->Add(phi); + } + } +} + + void HGraph::InferTypes(ZoneList<HValue*>* worklist) { BitVector in_worklist(GetMaximumValueID()); for (int i = 0; i < worklist->length(); ++i) { @@ -1330,6 +1347,7 @@ class HGlobalValueNumberer BASE_EMBEDDED { explicit HGlobalValueNumberer(HGraph* graph, CompilationInfo* info) : graph_(graph), info_(info), + removed_side_effects_(false), block_side_effects_(graph->blocks()->length()), loop_side_effects_(graph->blocks()->length()), visited_on_paths_(graph->zone(), graph->blocks()->length()) { @@ -1341,7 +1359,8 @@ class HGlobalValueNumberer BASE_EMBEDDED { ASSERT(!info_->isolate()->heap()->allow_allocation(true)); } - void Analyze(); + // Returns true if values with side effects are removed. + bool Analyze(); private: int CollectSideEffectsOnPathsToDominatedBlock(HBasicBlock* dominator, @@ -1361,6 +1380,7 @@ class HGlobalValueNumberer BASE_EMBEDDED { HGraph* graph_; CompilationInfo* info_; + bool removed_side_effects_; // A map of block IDs to their side effects. ZoneList<int> block_side_effects_; @@ -1374,13 +1394,14 @@ class HGlobalValueNumberer BASE_EMBEDDED { }; -void HGlobalValueNumberer::Analyze() { +bool HGlobalValueNumberer::Analyze() { ComputeBlockSideEffects(); if (FLAG_loop_invariant_code_motion) { LoopInvariantCodeMotion(); } HValueMap* map = new(zone()) HValueMap(); AnalyzeBlock(graph_->entry_block(), map); + return removed_side_effects_; } @@ -1514,11 +1535,12 @@ void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) { HInstruction* next = instr->next(); int flags = instr->ChangesFlags(); if (flags != 0) { - ASSERT(!instr->CheckFlag(HValue::kUseGVN)); // Clear all instructions in the map that are affected by side effects. map->Kill(flags); TraceGVN("Instruction %d kills\n", instr->id()); - } else if (instr->CheckFlag(HValue::kUseGVN)) { + } + if (instr->CheckFlag(HValue::kUseGVN)) { + ASSERT(!instr->HasObservableSideEffects()); HValue* other = map->Lookup(instr); if (other != NULL) { ASSERT(instr->Equals(other) && other->Equals(instr)); @@ -1527,6 +1549,7 @@ void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) { instr->Mnemonic(), other->id(), other->Mnemonic()); + if (instr->HasSideEffects()) removed_side_effects_ = true; instr->DeleteAndReplaceWith(other); } else { map->Add(instr); @@ -1656,7 +1679,7 @@ Representation HInferRepresentation::TryChange(HValue* value) { } // Prefer unboxing over boxing, the latter is more expensive. - if (tagged_count > non_tagged_count) Representation::None(); + if (tagged_count > non_tagged_count) return Representation::None(); // Prefer Integer32 over Double, if possible. if (int32_count > 0 && value->IsConvertibleToInteger()) { @@ -1851,7 +1874,7 @@ void HGraph::InsertRepresentationChangeForUse(HValue* value, } if (new_value == NULL) { - new_value = new(zone()) HChange(value, value->representation(), to, + new_value = new(zone()) HChange(value, to, is_truncating, deoptimize_on_undefined); } @@ -1996,11 +2019,13 @@ void HGraph::ComputeMinusZeroChecks() { // a (possibly inlined) function. FunctionState::FunctionState(HGraphBuilder* owner, CompilationInfo* info, - TypeFeedbackOracle* oracle) + TypeFeedbackOracle* oracle, + bool drop_extra) : owner_(owner), compilation_info_(info), oracle_(oracle), call_context_(NULL), + drop_extra_(drop_extra), function_return_(NULL), test_context_(NULL), outer_(owner->function_state()) { @@ -2090,12 +2115,12 @@ void TestContext::ReturnValue(HValue* value) { void EffectContext::ReturnInstruction(HInstruction* instr, int ast_id) { ASSERT(!instr->IsControlInstruction()); owner()->AddInstruction(instr); - if (instr->HasSideEffects()) owner()->AddSimulate(ast_id); + if (instr->HasObservableSideEffects()) owner()->AddSimulate(ast_id); } void EffectContext::ReturnControl(HControlInstruction* instr, int ast_id) { - ASSERT(!instr->HasSideEffects()); + ASSERT(!instr->HasObservableSideEffects()); HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock(); HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock(); instr->SetSuccessorAt(0, empty_true); @@ -2113,12 +2138,12 @@ void ValueContext::ReturnInstruction(HInstruction* instr, int ast_id) { } owner()->AddInstruction(instr); owner()->Push(instr); - if (instr->HasSideEffects()) owner()->AddSimulate(ast_id); + if (instr->HasObservableSideEffects()) owner()->AddSimulate(ast_id); } void ValueContext::ReturnControl(HControlInstruction* instr, int ast_id) { - ASSERT(!instr->HasSideEffects()); + ASSERT(!instr->HasObservableSideEffects()); if (!arguments_allowed() && instr->CheckFlag(HValue::kIsArguments)) { return owner()->Bailout("bad value context for arguments object value"); } @@ -2143,7 +2168,7 @@ void TestContext::ReturnInstruction(HInstruction* instr, int ast_id) { 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()) { + if (instr->HasObservableSideEffects()) { builder->Push(instr); builder->AddSimulate(ast_id); builder->Pop(); @@ -2153,14 +2178,14 @@ void TestContext::ReturnInstruction(HInstruction* instr, int ast_id) { void TestContext::ReturnControl(HControlInstruction* instr, int ast_id) { - ASSERT(!instr->HasSideEffects()); + ASSERT(!instr->HasObservableSideEffects()); HBasicBlock* empty_true = owner()->graph()->CreateBasicBlock(); HBasicBlock* empty_false = owner()->graph()->CreateBasicBlock(); instr->SetSuccessorAt(0, empty_true); instr->SetSuccessorAt(1, empty_false); owner()->current_block()->Finish(instr); - empty_true->Goto(if_true()); - empty_false->Goto(if_false()); + empty_true->Goto(if_true(), owner()->function_state()->drop_extra()); + empty_false->Goto(if_false(), owner()->function_state()->drop_extra()); owner()->set_current_block(NULL); } @@ -2181,8 +2206,8 @@ void TestContext::BuildBranch(HValue* value) { HBranch* test = new(zone()) HBranch(value, empty_true, empty_false, expected); builder->current_block()->Finish(test); - empty_true->Goto(if_true()); - empty_false->Goto(if_false()); + empty_true->Goto(if_true(), owner()->function_state()->drop_extra()); + empty_false->Goto(if_false(), owner()->function_state()->drop_extra()); builder->set_current_block(NULL); } @@ -2302,7 +2327,7 @@ HGraph* HGraphBuilder::CreateGraph() { // Handle implicit declaration of the function name in named function // expressions before other declarations. if (scope->is_function_scope() && scope->function() != NULL) { - HandleDeclaration(scope->function(), Variable::CONST, NULL); + HandleDeclaration(scope->function(), CONST, NULL); } VisitDeclarations(scope->declarations()); AddSimulate(AstNode::kDeclarationsId); @@ -2323,17 +2348,24 @@ HGraph* HGraphBuilder::CreateGraph() { graph()->OrderBlocks(); graph()->AssignDominators(); + +#ifdef DEBUG + // Do a full verify after building the graph and computing dominators. + graph()->Verify(true); +#endif + graph()->PropagateDeoptimizingMark(); - graph()->EliminateRedundantPhis(); - if (!graph()->CheckPhis()) { - Bailout("Unsupported phi use of arguments object"); + if (!graph()->CheckConstPhiUses()) { + Bailout("Unsupported phi use of const variable"); return NULL; } - if (FLAG_eliminate_dead_phis) graph()->EliminateUnreachablePhis(); - if (!graph()->CollectPhis()) { - Bailout("Unsupported phi use of uninitialized constant"); + graph()->EliminateRedundantPhis(); + if (!graph()->CheckArgumentsPhiUses()) { + Bailout("Unsupported phi use of arguments"); return NULL; } + if (FLAG_eliminate_dead_phis) graph()->EliminateUnreachablePhis(); + graph()->CollectPhis(); HInferRepresentation rep(graph()); rep.Analyze(); @@ -2348,7 +2380,13 @@ HGraph* HGraphBuilder::CreateGraph() { if (FLAG_use_gvn) { HPhase phase("Global value numbering", graph()); HGlobalValueNumberer gvn(graph(), info()); - gvn.Analyze(); + bool removed_side_effects = gvn.Analyze(); + // Trigger a second analysis pass to further eliminate duplicate values that + // could only be discovered by removing side-effect-generating instructions + // during the first pass. + if (FLAG_smi_only_arrays && removed_side_effects) { + gvn.Analyze(); + } } if (FLAG_use_range) { @@ -2636,12 +2674,14 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) { test->if_false()); } else if (context->IsEffect()) { CHECK_ALIVE(VisitForEffect(stmt->expression())); - current_block()->Goto(function_return()); + current_block()->Goto(function_return(), function_state()->drop_extra()); } else { ASSERT(context->IsValue()); CHECK_ALIVE(VisitForValue(stmt->expression())); HValue* return_value = environment()->Pop(); - current_block()->AddLeaveInlined(return_value, function_return()); + current_block()->AddLeaveInlined(return_value, + function_return(), + function_state()->drop_extra()); } set_current_block(NULL); } @@ -2669,43 +2709,95 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { return Bailout("SwitchStatement: too many clauses"); } + HValue* context = environment()->LookupContext(); + CHECK_ALIVE(VisitForValue(stmt->tag())); AddSimulate(stmt->EntryId()); HValue* tag_value = Pop(); HBasicBlock* first_test_block = current_block(); - // 1. Build all the tests, with dangling true branches. Unconditionally - // deoptimize if we encounter a non-smi comparison. + SwitchType switch_type = UNKNOWN_SWITCH; + + // 1. Extract clause type for (int i = 0; i < clause_count; ++i) { CaseClause* clause = clauses->at(i); if (clause->is_default()) continue; - if (!clause->label()->IsSmiLiteral()) { - return Bailout("SwitchStatement: non-literal switch label"); + + if (switch_type == UNKNOWN_SWITCH) { + if (clause->label()->IsSmiLiteral()) { + switch_type = SMI_SWITCH; + } else if (clause->label()->IsStringLiteral()) { + switch_type = STRING_SWITCH; + } else { + return Bailout("SwitchStatement: non-literal switch label"); + } + } else if ((switch_type == STRING_SWITCH && + !clause->label()->IsStringLiteral()) || + (switch_type == SMI_SWITCH && + !clause->label()->IsSmiLiteral())) { + return Bailout("SwitchStatemnt: mixed label types are not supported"); } + } - // Unconditionally deoptimize on the first non-smi compare. - clause->RecordTypeFeedback(oracle()); - if (!clause->IsSmiCompare()) { - // Finish with deoptimize and add uses of enviroment values to - // account for invisible uses. - current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll); - set_current_block(NULL); - break; + HUnaryControlInstruction* string_check = NULL; + HBasicBlock* not_string_block = NULL; + + // Test switch's tag value if all clauses are string literals + if (switch_type == STRING_SWITCH) { + string_check = new(zone()) HIsStringAndBranch(tag_value); + first_test_block = graph()->CreateBasicBlock(); + not_string_block = graph()->CreateBasicBlock(); + + string_check->SetSuccessorAt(0, first_test_block); + string_check->SetSuccessorAt(1, not_string_block); + current_block()->Finish(string_check); + + set_current_block(first_test_block); + } + + // 2. Build all the tests, with dangling true branches + for (int i = 0; i < clause_count; ++i) { + CaseClause* clause = clauses->at(i); + if (clause->is_default()) continue; + + if (switch_type == SMI_SWITCH) { + clause->RecordTypeFeedback(oracle()); } - // Otherwise generate a compare and branch. + // Generate a compare and branch. CHECK_ALIVE(VisitForValue(clause->label())); HValue* label_value = Pop(); - HCompareIDAndBranch* compare = - new(zone()) HCompareIDAndBranch(tag_value, - label_value, - Token::EQ_STRICT); - compare->SetInputRepresentation(Representation::Integer32()); - HBasicBlock* body_block = graph()->CreateBasicBlock(); + HBasicBlock* next_test_block = graph()->CreateBasicBlock(); + HBasicBlock* body_block = graph()->CreateBasicBlock(); + + HControlInstruction* compare; + + if (switch_type == SMI_SWITCH) { + if (!clause->IsSmiCompare()) { + // Finish with deoptimize and add uses of enviroment values to + // account for invisible uses. + current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll); + set_current_block(NULL); + break; + } + + HCompareIDAndBranch* compare_ = + new(zone()) HCompareIDAndBranch(tag_value, + label_value, + Token::EQ_STRICT); + compare_->SetInputRepresentation(Representation::Integer32()); + compare = compare_; + } else { + compare = new(zone()) HStringCompareAndBranch(context, tag_value, + label_value, + Token::EQ_STRICT); + } + compare->SetSuccessorAt(0, body_block); compare->SetSuccessorAt(1, next_test_block); current_block()->Finish(compare); + set_current_block(next_test_block); } @@ -2713,10 +2805,15 @@ void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { // exit. This block is NULL if we deoptimized. HBasicBlock* last_block = current_block(); - // 2. Loop over the clauses and the linked list of tests in lockstep, + if (not_string_block != NULL) { + last_block = CreateJoin(last_block, not_string_block, stmt->ExitId()); + } + + // 3. Loop over the clauses and the linked list of tests in lockstep, // translating the clause bodies. HBasicBlock* curr_test_block = first_test_block; HBasicBlock* fall_through_block = NULL; + BreakAndContinueInfo break_info(stmt); { BreakAndContinueScope push(&break_info, this); for (int i = 0; i < clause_count; ++i) { @@ -3125,12 +3222,22 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); Variable* variable = expr->var(); - if (variable->mode() == Variable::LET) { + if (variable->mode() == LET) { return Bailout("reference to let variable"); } switch (variable->location()) { case Variable::UNALLOCATED: { - LookupResult lookup; + // Handle known global constants like 'undefined' specially to avoid a + // load from a global cell for them. + Handle<Object> constant_value = + isolate()->factory()->GlobalConstantFor(variable->name()); + if (!constant_value.is_null()) { + HConstant* instr = + new(zone()) HConstant(constant_value, Representation::Tagged()); + return ast_context()->ReturnInstruction(instr, expr->id()); + } + + LookupResult lookup(isolate()); GlobalPropertyAccess type = LookupGlobalProperty(variable, &lookup, false); @@ -3142,8 +3249,8 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { if (type == kUseCell) { Handle<GlobalObject> global(info()->global_object()); Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup)); - bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly(); - HLoadGlobalCell* instr = new(zone()) HLoadGlobalCell(cell, check_hole); + HLoadGlobalCell* instr = + new(zone()) HLoadGlobalCell(cell, lookup.GetPropertyDetails()); return ast_context()->ReturnInstruction(instr, expr->id()); } else { HValue* context = environment()->LookupContext(); @@ -3162,7 +3269,7 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { case Variable::PARAMETER: case Variable::LOCAL: { HValue* value = environment()->Lookup(variable); - if (variable->mode() == Variable::CONST && + if (variable->mode() == CONST && value == graph()->GetConstantHole()) { return Bailout("reference to uninitialized const variable"); } @@ -3170,7 +3277,7 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { } case Variable::CONTEXT: { - if (variable->mode() == Variable::CONST) { + if (variable->mode() == CONST) { return Bailout("reference to const context slot"); } HValue* context = BuildContextChainWalk(variable); @@ -3209,18 +3316,78 @@ void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { } +// Determines whether the given object literal boilerplate satisfies all +// limits to be considered for fast deep-copying and computes the total +// size of all objects that are part of the graph. +static bool IsFastObjectLiteral(Handle<JSObject> boilerplate, + int max_depth, + int* max_properties, + int* total_size) { + if (max_depth <= 0) return false; + + FixedArrayBase* elements = boilerplate->elements(); + if (elements->length() > 0 && + elements->map() != HEAP->fixed_cow_array_map()) { + return false; + } + + FixedArray* properties = boilerplate->properties(); + if (properties->length() > 0) { + return false; + } else { + int nof = boilerplate->map()->inobject_properties(); + for (int i = 0; i < nof; i++) { + if ((*max_properties)-- <= 0) return false; + Handle<Object> value(boilerplate->InObjectPropertyAt(i)); + if (value->IsJSObject()) { + Handle<JSObject> value_object = Handle<JSObject>::cast(value); + if (!IsFastObjectLiteral(value_object, + max_depth - 1, + max_properties, + total_size)) { + return false; + } + } + } + } + + *total_size += boilerplate->map()->instance_size(); + return true; +} + + void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); + Handle<JSFunction> closure = function_state()->compilation_info()->closure(); HValue* context = environment()->LookupContext(); - HObjectLiteral* literal = - new(zone()) HObjectLiteral(context, - expr->constant_properties(), - expr->fast_elements(), - expr->literal_index(), - expr->depth(), - expr->has_function()); + HInstruction* literal; + + // Check whether to use fast or slow deep-copying for boilerplate. + int total_size = 0; + int max_properties = HObjectLiteralFast::kMaxObjectLiteralProperties; + Handle<Object> boilerplate(closure->literals()->get(expr->literal_index())); + if (boilerplate->IsJSObject() && + IsFastObjectLiteral(Handle<JSObject>::cast(boilerplate), + HObjectLiteralFast::kMaxObjectLiteralDepth, + &max_properties, + &total_size)) { + Handle<JSObject> boilerplate_object = Handle<JSObject>::cast(boilerplate); + literal = new(zone()) HObjectLiteralFast(context, + boilerplate_object, + total_size, + expr->literal_index(), + expr->depth()); + } else { + literal = new(zone()) HObjectLiteralGeneric(context, + expr->constant_properties(), + expr->fast_elements(), + expr->literal_index(), + expr->depth(), + expr->has_function()); + } + // The object is expected in the bailout environment during computation // of the property values and is the value of the entire expression. PushAndAdd(literal); @@ -3250,7 +3417,7 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { literal, name, value, - function_strict_mode()); + function_strict_mode_flag()); AddInstruction(store); AddSimulate(key->id()); } else { @@ -3311,16 +3478,49 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { HValue* value = Pop(); if (!Smi::IsValid(i)) return Bailout("Non-smi key in array literal"); - // Load the elements array before the first store. - if (elements == NULL) { - elements = new(zone()) HLoadElements(literal); - AddInstruction(elements); - } + elements = new(zone()) HLoadElements(literal); + AddInstruction(elements); HValue* key = AddInstruction( new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)), Representation::Integer32())); + HInstruction* elements_kind = + AddInstruction(new(zone()) HElementsKind(literal)); + HBasicBlock* store_fast = graph()->CreateBasicBlock(); + // Two empty blocks to satisfy edge split form. + HBasicBlock* store_fast_edgesplit1 = graph()->CreateBasicBlock(); + HBasicBlock* store_fast_edgesplit2 = graph()->CreateBasicBlock(); + HBasicBlock* store_generic = graph()->CreateBasicBlock(); + HBasicBlock* check_smi_only_elements = graph()->CreateBasicBlock(); + HBasicBlock* join = graph()->CreateBasicBlock(); + + HIsSmiAndBranch* smicheck = new(zone()) HIsSmiAndBranch(value); + smicheck->SetSuccessorAt(0, store_fast_edgesplit1); + smicheck->SetSuccessorAt(1, check_smi_only_elements); + current_block()->Finish(smicheck); + store_fast_edgesplit1->Finish(new(zone()) HGoto(store_fast)); + + set_current_block(check_smi_only_elements); + HCompareConstantEqAndBranch* smi_elements_check = + new(zone()) HCompareConstantEqAndBranch(elements_kind, + FAST_ELEMENTS, + Token::EQ_STRICT); + smi_elements_check->SetSuccessorAt(0, store_fast_edgesplit2); + smi_elements_check->SetSuccessorAt(1, store_generic); + current_block()->Finish(smi_elements_check); + store_fast_edgesplit2->Finish(new(zone()) HGoto(store_fast)); + + set_current_block(store_fast); AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value)); + store_fast->Goto(join); + + set_current_block(store_generic); + AddInstruction(BuildStoreKeyedGeneric(literal, key, value)); + store_generic->Goto(join); + + join->SetJoinId(expr->id()); + set_current_block(join); + AddSimulate(expr->GetIdForElement(i)); } return ast_context()->ReturnValue(Pop()); @@ -3395,7 +3595,7 @@ HInstruction* HGraphBuilder::BuildStoreNamedGeneric(HValue* object, object, name, value, - function_strict_mode()); + function_strict_mode_flag()); } @@ -3409,7 +3609,7 @@ HInstruction* HGraphBuilder::BuildStoreNamed(HValue* object, Handle<String> name = Handle<String>::cast(key->handle()); ASSERT(!name.is_null()); - LookupResult lookup; + LookupResult lookup(isolate()); SmallMapList* types = expr->GetReceiverTypes(); bool is_monomorphic = expr->IsMonomorphic() && ComputeStoredField(types->first(), name, &lookup); @@ -3433,7 +3633,7 @@ void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr, HBasicBlock* join = NULL; for (int i = 0; i < types->length() && count < kMaxStorePolymorphism; ++i) { Handle<Map> map = types->at(i); - LookupResult lookup; + LookupResult lookup(isolate()); if (ComputeStoredField(map, name, &lookup)) { if (count == 0) { AddInstruction(new(zone()) HCheckNonSmi(object)); // Only needed once. @@ -3476,7 +3676,7 @@ void HGraphBuilder::HandlePolymorphicStoreNamedField(Assignment* expr, // The HSimulate for the store should not see the stored value in // effect contexts (it is not materialized at expr->id() in the // unoptimized code). - if (instr->HasSideEffects()) { + if (instr->HasObservableSideEffects()) { if (ast_context()->IsEffect()) { AddSimulate(expr->id()); } else { @@ -3516,7 +3716,7 @@ void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) { ASSERT(!name.is_null()); SmallMapList* types = expr->GetReceiverTypes(); - LookupResult lookup; + LookupResult lookup(isolate()); if (expr->IsMonomorphic()) { instr = BuildStoreNamed(object, value, expr); @@ -3549,7 +3749,7 @@ void HGraphBuilder::HandlePropertyAssignment(Assignment* expr) { Push(value); instr->set_position(expr->position()); AddInstruction(instr); - if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); + if (instr->HasObservableSideEffects()) AddSimulate(expr->AssignmentId()); return ast_context()->ReturnValue(Pop()); } @@ -3561,16 +3761,16 @@ void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var, HValue* value, int position, int ast_id) { - LookupResult lookup; + LookupResult lookup(isolate()); GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true); if (type == kUseCell) { - bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly(); Handle<GlobalObject> global(info()->global_object()); Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup)); - HInstruction* instr = new(zone()) HStoreGlobalCell(value, cell, check_hole); + HInstruction* instr = + new(zone()) HStoreGlobalCell(value, cell, lookup.GetPropertyDetails()); instr->set_position(position); AddInstruction(instr); - if (instr->HasSideEffects()) AddSimulate(ast_id); + if (instr->HasObservableSideEffects()) AddSimulate(ast_id); } else { HValue* context = environment()->LookupContext(); HGlobalObject* global_object = new(zone()) HGlobalObject(context); @@ -3580,11 +3780,11 @@ void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var, global_object, var->name(), value, - function_strict_mode()); + function_strict_mode_flag()); instr->set_position(position); AddInstruction(instr); - ASSERT(instr->HasSideEffects()); - if (instr->HasSideEffects()) AddSimulate(ast_id); + ASSERT(instr->HasObservableSideEffects()); + if (instr->HasObservableSideEffects()) AddSimulate(ast_id); } } @@ -3601,7 +3801,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { if (proxy != NULL) { Variable* var = proxy->var(); - if (var->mode() == Variable::CONST || var->mode() == Variable::LET) { + if (var->mode() == CONST || var->mode() == LET) { return Bailout("unsupported let or const compound assignment"); } @@ -3641,7 +3841,9 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { HStoreContextSlot* instr = new(zone()) HStoreContextSlot(context, var->index(), Top()); AddInstruction(instr); - if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); + if (instr->HasObservableSideEffects()) { + AddSimulate(expr->AssignmentId()); + } break; } @@ -3667,7 +3869,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { load = BuildLoadNamedGeneric(obj, prop); } PushAndAdd(load); - if (load->HasSideEffects()) AddSimulate(expr->CompoundLoadId()); + if (load->HasObservableSideEffects()) AddSimulate(expr->CompoundLoadId()); CHECK_ALIVE(VisitForValue(expr->value())); HValue* right = Pop(); @@ -3675,14 +3877,14 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { HInstruction* instr = BuildBinaryOperation(operation, left, right); PushAndAdd(instr); - if (instr->HasSideEffects()) AddSimulate(operation->id()); + if (instr->HasObservableSideEffects()) AddSimulate(operation->id()); HInstruction* store = BuildStoreNamed(obj, instr, prop); AddInstruction(store); // Drop the simulated receiver and value. Return the value. Drop(2); Push(instr); - if (store->HasSideEffects()) AddSimulate(expr->AssignmentId()); + if (store->HasObservableSideEffects()) AddSimulate(expr->AssignmentId()); return ast_context()->ReturnValue(Pop()); } else { @@ -3707,7 +3909,7 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { HInstruction* instr = BuildBinaryOperation(operation, left, right); PushAndAdd(instr); - if (instr->HasSideEffects()) AddSimulate(operation->id()); + if (instr->HasObservableSideEffects()) AddSimulate(operation->id()); expr->RecordTypeFeedback(oracle()); HandleKeyedElementAccess(obj, key, instr, expr, expr->AssignmentId(), @@ -3746,7 +3948,7 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { HandlePropertyAssignment(expr); } else if (proxy != NULL) { Variable* var = proxy->var(); - if (var->mode() == Variable::CONST) { + if (var->mode() == CONST) { if (expr->op() != Token::INIT_CONST) { return Bailout("non-initializer assignment to const"); } @@ -3757,7 +3959,7 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { // variables (e.g. initialization inside a loop). HValue* old_value = environment()->Lookup(var); AddInstruction(new HUseConst(old_value)); - } else if (var->mode() == Variable::LET) { + } else if (var->mode() == LET) { return Bailout("unsupported assignment to let"); } @@ -3785,7 +3987,7 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { } case Variable::CONTEXT: { - ASSERT(var->mode() != Variable::CONST); + ASSERT(var->mode() != CONST); // Bail out if we try to mutate a parameter value in a function using // the arguments object. We do not (yet) correctly handle the // arguments property of the function. @@ -3805,7 +4007,9 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { HStoreContextSlot* instr = new(zone()) HStoreContextSlot(context, var->index(), Top()); AddInstruction(instr); - if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); + if (instr->HasObservableSideEffects()) { + AddSimulate(expr->AssignmentId()); + } return ast_context()->ReturnValue(Pop()); } @@ -3876,7 +4080,7 @@ HInstruction* HGraphBuilder::BuildLoadNamed(HValue* obj, Property* expr, Handle<Map> map, Handle<String> name) { - LookupResult lookup; + LookupResult lookup(isolate()); map->LookupInDescriptors(NULL, *name, &lookup); if (lookup.IsProperty() && lookup.type() == FIELD) { return BuildLoadNamedField(obj, @@ -3931,6 +4135,7 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess( case EXTERNAL_FLOAT_ELEMENTS: case EXTERNAL_DOUBLE_ELEMENTS: break; + case FAST_SMI_ONLY_ELEMENTS: case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: case DICTIONARY_ELEMENTS: @@ -3947,24 +4152,48 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess( } +HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements, + HValue* checked_key, + HValue* val, + ElementsKind elements_kind, + bool is_store) { + if (is_store) { + ASSERT(val != NULL); + if (elements_kind == FAST_DOUBLE_ELEMENTS) { + return new(zone()) HStoreKeyedFastDoubleElement( + elements, checked_key, val); + } else { // FAST_ELEMENTS or FAST_SMI_ONLY_ELEMENTS. + return new(zone()) HStoreKeyedFastElement( + elements, checked_key, val, elements_kind); + } + } + // It's an element load (!is_store). + if (elements_kind == FAST_DOUBLE_ELEMENTS) { + return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key); + } else { // FAST_ELEMENTS or FAST_SMI_ONLY_ELEMENTS. + return new(zone()) HLoadKeyedFastElement(elements, checked_key); + } +} + + HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object, HValue* key, HValue* val, - Expression* expr, + Handle<Map> map, bool is_store) { - ASSERT(expr->IsMonomorphic()); - Handle<Map> map = expr->GetMonomorphicReceiverType(); - if (!map->has_fast_elements() && - !map->has_fast_double_elements() && + HInstruction* mapcheck = AddInstruction(new(zone()) HCheckMap(object, map)); + bool fast_smi_only_elements = map->has_fast_smi_only_elements(); + bool fast_elements = map->has_fast_elements(); + bool fast_double_elements = map->has_fast_double_elements(); + if (!fast_smi_only_elements && + !fast_elements && + !fast_double_elements && !map->has_external_array_elements()) { return is_store ? BuildStoreKeyedGeneric(object, key, val) : BuildLoadKeyedGeneric(object, key); } - AddInstruction(new(zone()) HCheckNonSmi(object)); - HInstruction* mapcheck = AddInstruction(new(zone()) HCheckMap(object, map)); HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object)); - bool fast_double_elements = map->has_fast_double_elements(); - if (is_store && map->has_fast_elements()) { + if (is_store && (fast_elements || fast_smi_only_elements)) { AddInstruction(new(zone()) HCheckMap( elements, isolate()->factory()->fixed_array_map())); } @@ -3979,28 +4208,15 @@ HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object, return BuildExternalArrayElementAccess(external_elements, checked_key, val, map->elements_kind(), is_store); } - ASSERT(map->has_fast_elements() || fast_double_elements); + ASSERT(fast_smi_only_elements || fast_elements || fast_double_elements); if (map->instance_type() == JS_ARRAY_TYPE) { length = AddInstruction(new(zone()) HJSArrayLength(object, mapcheck)); } else { length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); } checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); - if (is_store) { - if (fast_double_elements) { - return new(zone()) HStoreKeyedFastDoubleElement(elements, - checked_key, - val); - } else { - return new(zone()) HStoreKeyedFastElement(elements, checked_key, val); - } - } else { - if (fast_double_elements) { - return new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key); - } else { - return new(zone()) HLoadKeyedFastElement(elements, checked_key); - } - } + return BuildFastElementAccess(elements, checked_key, val, + map->elements_kind(), is_store); } @@ -4014,7 +4230,6 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, bool* has_side_effects) { *has_side_effects = false; AddInstruction(new(zone()) HCheckNonSmi(object)); - AddInstruction(HCheckInstanceType::NewIsSpecObject(object)); SmallMapList* maps = prop->GetReceiverTypes(); bool todo_external_array = false; @@ -4024,15 +4239,55 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, type_todo[i] = false; } + // Elements_kind transition support. + MapHandleList transition_target(maps->length()); + // Collect possible transition targets. + MapHandleList possible_transitioned_maps(maps->length()); + for (int i = 0; i < maps->length(); ++i) { + Handle<Map> map = maps->at(i); + ElementsKind elements_kind = map->elements_kind(); + if (elements_kind == FAST_DOUBLE_ELEMENTS || + elements_kind == FAST_ELEMENTS) { + possible_transitioned_maps.Add(map); + } + } + // Get transition target for each map (NULL == no transition). + for (int i = 0; i < maps->length(); ++i) { + Handle<Map> map = maps->at(i); + Handle<Map> transitioned_map = + map->FindTransitionedMap(&possible_transitioned_maps); + transition_target.Add(transitioned_map); + } + + int num_untransitionable_maps = 0; + Handle<Map> untransitionable_map; for (int i = 0; i < maps->length(); ++i) { - ASSERT(maps->at(i)->IsMap()); - type_todo[maps->at(i)->elements_kind()] = true; - if (maps->at(i)->elements_kind() - >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) { - todo_external_array = true; + Handle<Map> map = maps->at(i); + ASSERT(map->IsMap()); + if (!transition_target.at(i).is_null()) { + object = AddInstruction(new(zone()) HTransitionElementsKind( + object, map, transition_target.at(i))); + } else { + type_todo[map->elements_kind()] = true; + if (map->elements_kind() >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) { + todo_external_array = true; + } + num_untransitionable_maps++; + untransitionable_map = map; } } + // If only one map is left after transitioning, handle this case + // monomorphically. + if (num_untransitionable_maps == 1) { + HInstruction* instr = AddInstruction(BuildMonomorphicElementAccess( + object, key, val, untransitionable_map, is_store)); + *has_side_effects |= instr->HasObservableSideEffects(); + instr->set_position(position); + return is_store ? NULL : instr; + } + + AddInstruction(HCheckInstanceType::NewIsSpecObject(object)); HBasicBlock* join = graph()->CreateBasicBlock(); HInstruction* elements_kind_instr = @@ -4042,14 +4297,20 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, HLoadExternalArrayPointer* external_elements = NULL; HInstruction* checked_key = NULL; - // FAST_ELEMENTS is assumed to be the first case. - STATIC_ASSERT(FAST_ELEMENTS == 0); + // Generated code assumes that FAST_SMI_ONLY_ELEMENTS, FAST_ELEMENTS, + // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS are handled before external + // arrays. + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); + STATIC_ASSERT(FAST_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); + STATIC_ASSERT(FAST_DOUBLE_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); + STATIC_ASSERT(DICTIONARY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); - for (ElementsKind elements_kind = FAST_ELEMENTS; + for (ElementsKind elements_kind = FIRST_ELEMENTS_KIND; elements_kind <= LAST_ELEMENTS_KIND; elements_kind = ElementsKind(elements_kind + 1)) { - // After having handled FAST_ELEMENTS and DICTIONARY_ELEMENTS, we - // need to add some code that's executed for all external array cases. + // After having handled FAST_ELEMENTS, FAST_SMI_ONLY_ELEMENTS, + // FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS, we need to add some code + // that's executed for all external array cases. STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND == LAST_ELEMENTS_KIND); if (elements_kind == FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND @@ -4071,15 +4332,25 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, set_current_block(if_true); HInstruction* access; - if (elements_kind == FAST_ELEMENTS || + if (elements_kind == FAST_SMI_ONLY_ELEMENTS || + elements_kind == FAST_ELEMENTS || elements_kind == FAST_DOUBLE_ELEMENTS) { - bool fast_double_elements = - elements_kind == FAST_DOUBLE_ELEMENTS; - if (is_store && elements_kind == FAST_ELEMENTS) { + if (is_store && elements_kind == FAST_SMI_ONLY_ELEMENTS) { + AddInstruction(new(zone()) HCheckSmi(val)); + } + if (is_store && elements_kind != FAST_DOUBLE_ELEMENTS) { AddInstruction(new(zone()) HCheckMap( elements, isolate()->factory()->fixed_array_map(), elements_kind_branch)); } + // TODO(jkummerow): The need for these two blocks could be avoided + // in one of two ways: + // (1) Introduce ElementsKinds for JSArrays that are distinct from + // those for fast objects. + // (2) Put the common instructions into a third "join" block. This + // requires additional AST IDs that we can deopt to from inside + // that join block. They must be added to the Property class (when + // it's a keyed property) and registered in the full codegen. HBasicBlock* if_jsarray = graph()->CreateBasicBlock(); HBasicBlock* if_fastobject = graph()->CreateBasicBlock(); HHasInstanceTypeAndBranch* typecheck = @@ -4089,30 +4360,16 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, current_block()->Finish(typecheck); set_current_block(if_jsarray); - HInstruction* length = new(zone()) HJSArrayLength(object, typecheck); - AddInstruction(length); + HInstruction* length; + length = AddInstruction(new(zone()) HJSArrayLength(object, typecheck)); checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); - if (is_store) { - if (fast_double_elements) { - access = AddInstruction( - new(zone()) HStoreKeyedFastDoubleElement(elements, - checked_key, - val)); - } else { - access = AddInstruction( - new(zone()) HStoreKeyedFastElement(elements, checked_key, val)); - } - } else { - if (fast_double_elements) { - access = AddInstruction( - new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key)); - } else { - access = AddInstruction( - new(zone()) HLoadKeyedFastElement(elements, checked_key)); - } + access = AddInstruction(BuildFastElementAccess( + elements, checked_key, val, elements_kind, is_store)); + if (!is_store) { Push(access); } - *has_side_effects |= access->HasSideEffects(); + + *has_side_effects |= access->HasObservableSideEffects(); if (position != -1) { access->set_position(position); } @@ -4121,25 +4378,8 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, set_current_block(if_fastobject); length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length)); - if (is_store) { - if (fast_double_elements) { - access = AddInstruction( - new(zone()) HStoreKeyedFastDoubleElement(elements, - checked_key, - val)); - } else { - access = AddInstruction( - new(zone()) HStoreKeyedFastElement(elements, checked_key, val)); - } - } else { - if (fast_double_elements) { - access = AddInstruction( - new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key)); - } else { - access = AddInstruction( - new(zone()) HLoadKeyedFastElement(elements, checked_key)); - } - } + access = AddInstruction(BuildFastElementAccess( + elements, checked_key, val, elements_kind, is_store)); } else if (elements_kind == DICTIONARY_ELEMENTS) { if (is_store) { access = AddInstruction(BuildStoreKeyedGeneric(object, key, val)); @@ -4150,7 +4390,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, access = AddInstruction(BuildExternalArrayElementAccess( external_elements, checked_key, val, elements_kind, is_store)); } - *has_side_effects |= access->HasSideEffects(); + *has_side_effects |= access->HasObservableSideEffects(); access->set_position(position); if (!is_store) { Push(access); @@ -4179,7 +4419,9 @@ HValue* HGraphBuilder::HandleKeyedElementAccess(HValue* obj, ASSERT(!expr->IsPropertyName()); HInstruction* instr = NULL; if (expr->IsMonomorphic()) { - instr = BuildMonomorphicElementAccess(obj, key, val, expr, is_store); + Handle<Map> map = expr->GetMonomorphicReceiverType(); + AddInstruction(new(zone()) HCheckNonSmi(obj)); + instr = BuildMonomorphicElementAccess(obj, key, val, map, is_store); } else if (expr->GetReceiverTypes() != NULL && !expr->GetReceiverTypes()->is_empty()) { return HandlePolymorphicElementAccess( @@ -4193,7 +4435,7 @@ HValue* HGraphBuilder::HandleKeyedElementAccess(HValue* obj, } instr->set_position(position); AddInstruction(instr); - *has_side_effects = instr->HasSideEffects(); + *has_side_effects = instr->HasObservableSideEffects(); return instr; } @@ -4207,7 +4449,7 @@ HInstruction* HGraphBuilder::BuildStoreKeyedGeneric(HValue* object, object, key, value, - function_strict_mode()); + function_strict_mode_flag()); } bool HGraphBuilder::TryArgumentsAccess(Property* expr) { @@ -4260,7 +4502,7 @@ void HGraphBuilder::VisitProperty(Property* expr) { CHECK_ALIVE(VisitForValue(expr->obj())); HInstruction* instr = NULL; - if (expr->IsArrayLength()) { + if (expr->AsProperty()->IsArrayLength()) { HValue* array = Pop(); AddInstruction(new(zone()) HCheckNonSmi(array)); HInstruction* mapcheck = @@ -4449,7 +4691,7 @@ void HGraphBuilder::TraceInline(Handle<JSFunction> target, } -bool HGraphBuilder::TryInline(Call* expr) { +bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) { if (!FLAG_use_inlining) return false; // The function call we are inlining is a method call if the call @@ -4477,7 +4719,8 @@ bool HGraphBuilder::TryInline(Call* expr) { return false; } - // No context change required. +#if !defined(V8_TARGET_ARCH_IA32) + // Target must be able to use caller's context. CompilationInfo* outer_info = info(); if (target->context() != outer_info->closure()->context() || outer_info->scope()->contains_with() || @@ -4485,6 +4728,8 @@ bool HGraphBuilder::TryInline(Call* expr) { TraceInline(target, caller, "target requires context change"); return false; } +#endif + // Don't inline deeper than kMaxInliningLevels calls. HEnvironment* env = environment(); @@ -4499,9 +4744,13 @@ bool HGraphBuilder::TryInline(Call* expr) { } // Don't inline recursive functions. - if (*target_shared == outer_info->closure()->shared()) { - TraceInline(target, caller, "target is recursive"); - return false; + for (FunctionState* state = function_state(); + state != NULL; + state = state->outer()) { + if (state->compilation_info()->closure()->shared() == *target_shared) { + TraceInline(target, caller, "target is recursive"); + return false; + } } // We don't want to add more than a certain number of nodes from inlining. @@ -4514,7 +4763,7 @@ bool HGraphBuilder::TryInline(Call* expr) { // Parse and allocate variables. CompilationInfo target_info(target); - if (!ParserApi::Parse(&target_info) || + if (!ParserApi::Parse(&target_info, kNoParsingFlags) || !Scope::Analyze(&target_info)) { if (target_info.isolate()->has_pending_exception()) { // Parse or scope error, never optimize this function. @@ -4574,11 +4823,11 @@ bool HGraphBuilder::TryInline(Call* expr) { TraceInline(target, caller, "could not generate deoptimization info"); return false; } - if (target_shared->scope_info() == SerializedScopeInfo::Empty()) { + if (target_shared->scope_info() == ScopeInfo::Empty()) { // The scope info might not have been set if a lazily compiled // function is inlined before being called for the first time. - Handle<SerializedScopeInfo> target_scope_info = - SerializedScopeInfo::Create(target_info.scope()); + Handle<ScopeInfo> target_scope_info = + ScopeInfo::Create(target_info.scope()); target_shared->set_scope_info(*target_scope_info); } target_shared->EnableDeoptimizationSupport(*target_info.code()); @@ -4596,8 +4845,12 @@ bool HGraphBuilder::TryInline(Call* expr) { ASSERT(target_shared->has_deoptimization_support()); TypeFeedbackOracle target_oracle( Handle<Code>(target_shared->code()), - Handle<Context>(target->context()->global_context())); - FunctionState target_state(this, &target_info, &target_oracle); + Handle<Context>(target->context()->global_context()), + isolate()); + // The function state is new-allocated because we need to delete it + // in two different places. + FunctionState* target_state = + new FunctionState(this, &target_info, &target_oracle, drop_extra); HConstant* undefined = graph()->GetConstantUndefined(); HEnvironment* inner_env = @@ -4605,6 +4858,17 @@ bool HGraphBuilder::TryInline(Call* expr) { function, undefined, call_kind); +#ifdef V8_TARGET_ARCH_IA32 + // IA32 only, overwrite the caller's context in the deoptimization + // environment with the correct one. + // + // TODO(kmillikin): implement the same inlining on other platforms so we + // can remove the unsightly ifdefs in this function. + HConstant* context = new HConstant(Handle<Context>(target->context()), + Representation::Tagged()); + AddInstruction(context); + inner_env->BindContext(context); +#endif HBasicBlock* body_entry = CreateBasicBlock(inner_env); current_block()->Goto(body_entry); body_entry->SetJoinId(expr->ReturnId()); @@ -4620,6 +4884,7 @@ bool HGraphBuilder::TryInline(Call* expr) { TraceInline(target, caller, "inline graph construction failed"); target_shared->DisableOptimization(*target); inline_bailout_ = true; + delete target_state; return true; } @@ -4635,9 +4900,11 @@ bool HGraphBuilder::TryInline(Call* expr) { ASSERT(function_return() != NULL); ASSERT(call_context()->IsEffect() || call_context()->IsValue()); if (call_context()->IsEffect()) { - current_block()->Goto(function_return()); + current_block()->Goto(function_return(), drop_extra); } else { - current_block()->AddLeaveInlined(undefined, function_return()); + current_block()->AddLeaveInlined(undefined, + function_return(), + drop_extra); } } else { // The graph builder assumes control can reach both branches of a @@ -4645,13 +4912,14 @@ bool HGraphBuilder::TryInline(Call* expr) { // simply jumping to the false target. // // TODO(3168478): refactor to avoid this. + ASSERT(call_context()->IsTest()); HBasicBlock* empty_true = graph()->CreateBasicBlock(); HBasicBlock* empty_false = graph()->CreateBasicBlock(); HBranch* test = new(zone()) HBranch(undefined, empty_true, empty_false); current_block()->Finish(test); - empty_true->Goto(inlined_test_context()->if_true()); - empty_false->Goto(inlined_test_context()->if_false()); + empty_true->Goto(inlined_test_context()->if_true(), drop_extra); + empty_false->Goto(inlined_test_context()->if_false(), drop_extra); } } @@ -4663,19 +4931,21 @@ bool HGraphBuilder::TryInline(Call* expr) { // Pop the return test context from the expression context stack. ASSERT(ast_context() == inlined_test_context()); ClearInlinedTestContext(); + delete target_state; // Forward to the real test context. if (if_true->HasPredecessor()) { if_true->SetJoinId(expr->id()); HBasicBlock* true_target = TestContext::cast(ast_context())->if_true(); - if_true->Goto(true_target); + if_true->Goto(true_target, function_state()->drop_extra()); } if (if_false->HasPredecessor()) { if_false->SetJoinId(expr->id()); HBasicBlock* false_target = TestContext::cast(ast_context())->if_false(); - if_false->Goto(false_target); + if_false->Goto(false_target, function_state()->drop_extra()); } set_current_block(NULL); + return true; } else if (function_return()->HasPredecessor()) { function_return()->SetJoinId(expr->id()); @@ -4683,7 +4953,7 @@ bool HGraphBuilder::TryInline(Call* expr) { } else { set_current_block(NULL); } - + delete target_state; return true; } @@ -4764,7 +5034,7 @@ bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr, AddInstruction(square_root); // MathPowHalf doesn't have side effects so there's no need for // an environment simulation here. - ASSERT(!square_root->HasSideEffects()); + ASSERT(!square_root->HasObservableSideEffects()); result = new(zone()) HDiv(context, double_one, square_root); } else if (exponent == 2.0) { result = new(zone()) HMul(context, left, left); @@ -4897,7 +5167,7 @@ void HGraphBuilder::VisitCall(Call* expr) { return; } - if (CallStubCompiler::HasCustomCallGenerator(*expr->target()) || + if (CallStubCompiler::HasCustomCallGenerator(expr->target()) || expr->check_type() != RECEIVER_MAP_CHECK) { // When the target has a custom call IC generator, use the IC, // because it is likely to generate better code. Also use the IC @@ -4925,8 +5195,8 @@ void HGraphBuilder::VisitCall(Call* expr) { } } else { + expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION); VariableProxy* proxy = expr->expression()->AsVariableProxy(); - // FIXME. bool global_call = proxy != NULL && proxy->var()->IsUnallocated(); if (global_call) { @@ -4935,7 +5205,7 @@ void HGraphBuilder::VisitCall(Call* expr) { // If there is a global property cell for the name at compile time and // access check is not enabled we assume that the function will not change // and generate optimized code for calling the function. - LookupResult lookup; + LookupResult lookup(isolate()); GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, false); if (type == kUseCell && !info()->global_object()->IsAccessCheckNeeded()) { @@ -4978,8 +5248,30 @@ void HGraphBuilder::VisitCall(Call* expr) { Drop(argument_count); } + } else if (expr->IsMonomorphic()) { + // The function is on the stack in the unoptimized code during + // evaluation of the arguments. + CHECK_ALIVE(VisitForValue(expr->expression())); + HValue* function = Top(); + HValue* context = environment()->LookupContext(); + HGlobalObject* global = new(zone()) HGlobalObject(context); + HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global); + AddInstruction(global); + PushAndAdd(receiver); + CHECK_ALIVE(VisitExpressions(expr->arguments())); + AddInstruction(new(zone()) HCheckFunction(function, expr->target())); + if (TryInline(expr, true)) { // Drop function from environment. + return; + } else { + call = PreProcessCall(new(zone()) HInvokeFunction(context, + function, + argument_count)); + Drop(1); // The function. + } + } else { - CHECK_ALIVE(VisitArgument(expr->expression())); + CHECK_ALIVE(VisitForValue(expr->expression())); + HValue* function = Top(); HValue* context = environment()->LookupContext(); HGlobalObject* global_object = new(zone()) HGlobalObject(context); HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global_object); @@ -4988,9 +5280,7 @@ void HGraphBuilder::VisitCall(Call* expr) { PushAndAdd(new(zone()) HPushArgument(receiver)); CHECK_ALIVE(VisitArgumentList(expr->arguments())); - // The function to call is treated as an argument to the call function - // stub. - call = new(zone()) HCallFunction(context, argument_count + 1); + call = new(zone()) HCallFunction(context, function, argument_count); Drop(argument_count + 1); } } @@ -5185,7 +5475,6 @@ void HGraphBuilder::VisitBitNot(UnaryOperation* expr) { void HGraphBuilder::VisitNot(UnaryOperation* expr) { - // TODO(svenpanne) Perhaps a switch/virtual function is nicer here. if (ast_context()->IsTest()) { TestContext* context = TestContext::cast(ast_context()); VisitForControl(expr->expression(), @@ -5207,7 +5496,7 @@ void HGraphBuilder::VisitNot(UnaryOperation* expr) { materialize_true)); if (materialize_false->HasPredecessor()) { - materialize_false->SetJoinId(expr->expression()->id()); + materialize_false->SetJoinId(expr->MaterializeFalseId()); set_current_block(materialize_false); Push(graph()->GetConstantFalse()); } else { @@ -5215,7 +5504,7 @@ void HGraphBuilder::VisitNot(UnaryOperation* expr) { } if (materialize_true->HasPredecessor()) { - materialize_true->SetJoinId(expr->expression()->id()); + materialize_true->SetJoinId(expr->MaterializeTrueId()); set_current_block(materialize_true); Push(graph()->GetConstantTrue()); } else { @@ -5284,7 +5573,7 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { if (proxy != NULL) { Variable* var = proxy->var(); - if (var->mode() == Variable::CONST) { + if (var->mode() == CONST) { return Bailout("unsupported count operation with const"); } // Argument of the count operation is a variable, not a property. @@ -5328,7 +5617,9 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { HStoreContextSlot* instr = new(zone()) HStoreContextSlot(context, var->index(), after); AddInstruction(instr); - if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); + if (instr->HasObservableSideEffects()) { + AddSimulate(expr->AssignmentId()); + } break; } @@ -5357,7 +5648,7 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { load = BuildLoadNamedGeneric(obj, prop); } PushAndAdd(load); - if (load->HasSideEffects()) AddSimulate(expr->CountId()); + if (load->HasObservableSideEffects()) AddSimulate(expr->CountId()); after = BuildIncrement(returns_original_input, expr); input = Pop(); @@ -5370,7 +5661,7 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { // necessary. environment()->SetExpressionStackAt(0, after); if (returns_original_input) environment()->SetExpressionStackAt(1, input); - if (store->HasSideEffects()) AddSimulate(expr->AssignmentId()); + if (store->HasObservableSideEffects()) AddSimulate(expr->AssignmentId()); } else { // Keyed property. @@ -5447,38 +5738,34 @@ HInstruction* HGraphBuilder::BuildBinaryOperation(BinaryOperation* expr, AddInstruction(HCheckInstanceType::NewIsString(right)); instr = new(zone()) HStringAdd(context, left, right); } else { - instr = new(zone()) HAdd(context, left, right); + instr = HAdd::NewHAdd(zone(), context, left, right); } break; case Token::SUB: - instr = new(zone()) HSub(context, left, right); + instr = HSub::NewHSub(zone(), context, left, right); break; case Token::MUL: - instr = new(zone()) HMul(context, left, right); + instr = HMul::NewHMul(zone(), context, left, right); break; case Token::MOD: - instr = new(zone()) HMod(context, left, right); + instr = HMod::NewHMod(zone(), context, left, right); break; case Token::DIV: - instr = new(zone()) HDiv(context, left, right); + instr = HDiv::NewHDiv(zone(), context, left, right); break; case Token::BIT_XOR: - instr = new(zone()) HBitXor(context, left, right); - break; case Token::BIT_AND: - instr = new(zone()) HBitAnd(context, left, right); - break; case Token::BIT_OR: - instr = new(zone()) HBitOr(context, left, right); + instr = HBitwise::NewHBitwise(zone(), expr->op(), context, left, right); break; case Token::SAR: - instr = new(zone()) HSar(context, left, right); + instr = HSar::NewHSar(zone(), context, left, right); break; case Token::SHR: - instr = new(zone()) HShr(context, left, right); + instr = HShr::NewHShr(zone(), context, left, right); break; case Token::SHL: - instr = new(zone()) HShl(context, left, right); + instr = HShl::NewHShl(zone(), context, left, right); break; default: UNREACHABLE(); @@ -5671,26 +5958,66 @@ Representation HGraphBuilder::ToRepresentation(TypeInfo info) { } -void HGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* compare_expr, - Expression* expr, +void HGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* expr, + HTypeof* typeof_expr, Handle<String> check) { - CHECK_ALIVE(VisitForTypeOf(expr)); - HValue* expr_value = Pop(); - HTypeofIsAndBranch* instr = new(zone()) HTypeofIsAndBranch(expr_value, check); - instr->set_position(compare_expr->position()); - return ast_context()->ReturnControl(instr, compare_expr->id()); + // Note: The HTypeof itself is removed during canonicalization, if possible. + HValue* value = typeof_expr->value(); + HTypeofIsAndBranch* instr = new(zone()) HTypeofIsAndBranch(value, check); + instr->set_position(expr->position()); + return ast_context()->ReturnControl(instr, expr->id()); } -void HGraphBuilder::HandleLiteralCompareUndefined( - CompareOperation* compare_expr, Expression* expr) { - CHECK_ALIVE(VisitForValue(expr)); - HValue* lhs = Pop(); - HValue* rhs = graph()->GetConstantUndefined(); - HCompareObjectEqAndBranch* instr = - new(zone()) HCompareObjectEqAndBranch(lhs, rhs); - instr->set_position(compare_expr->position()); - return ast_context()->ReturnControl(instr, compare_expr->id()); +static bool MatchLiteralCompareNil(HValue* left, + Token::Value op, + HValue* right, + Handle<Object> nil, + HValue** expr) { + if (left->IsConstant() && + HConstant::cast(left)->handle().is_identical_to(nil) && + Token::IsEqualityOp(op)) { + *expr = right; + return true; + } + return false; +} + + +static bool MatchLiteralCompareTypeof(HValue* left, + Token::Value op, + HValue* right, + HTypeof** typeof_expr, + Handle<String>* check) { + if (left->IsTypeof() && + Token::IsEqualityOp(op) && + right->IsConstant() && + HConstant::cast(right)->HasStringValue()) { + *typeof_expr = HTypeof::cast(left); + *check = Handle<String>::cast(HConstant::cast(right)->handle()); + return true; + } + return false; +} + + +static bool IsLiteralCompareTypeof(HValue* left, + Token::Value op, + HValue* right, + HTypeof** typeof_expr, + Handle<String>* check) { + return MatchLiteralCompareTypeof(left, op, right, typeof_expr, check) || + MatchLiteralCompareTypeof(right, op, left, typeof_expr, check); +} + + +static bool IsLiteralCompareNil(HValue* left, + Token::Value op, + HValue* right, + Handle<Object> nil, + HValue** expr) { + return MatchLiteralCompareNil(left, op, right, nil, expr) || + MatchLiteralCompareNil(right, op, left, nil, expr); } @@ -5711,21 +6038,9 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { return ast_context()->ReturnControl(instr, expr->id()); } - // Check for special cases that compare against literals. - Expression *sub_expr; - Handle<String> check; - if (expr->IsLiteralCompareTypeof(&sub_expr, &check)) { - HandleLiteralCompareTypeof(expr, sub_expr, check); - return; - } - - if (expr->IsLiteralCompareUndefined(&sub_expr)) { - HandleLiteralCompareUndefined(expr, sub_expr); - return; - } - TypeInfo type_info = oracle()->CompareType(expr); // Check if this expression was ever executed according to type feedback. + // Note that for the special typeof/null/undefined cases we get unknown here. if (type_info.IsUninitialized()) { AddInstruction(new(zone()) HSoftDeoptimize); current_block()->MarkAsDeoptimizing(); @@ -5740,6 +6055,20 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { HValue* left = Pop(); Token::Value op = expr->op(); + HTypeof* typeof_expr = NULL; + Handle<String> check; + if (IsLiteralCompareTypeof(left, op, right, &typeof_expr, &check)) { + return HandleLiteralCompareTypeof(expr, typeof_expr, check); + } + HValue* sub_expr = NULL; + Factory* f = graph()->isolate()->factory(); + if (IsLiteralCompareNil(left, op, right, f->undefined_value(), &sub_expr)) { + return HandleLiteralCompareNil(expr, sub_expr, kUndefinedValue); + } + if (IsLiteralCompareNil(left, op, right, f->null_value(), &sub_expr)) { + return HandleLiteralCompareNil(expr, sub_expr, kNullValue); + } + if (op == Token::INSTANCEOF) { // Check to see if the rhs of the instanceof is a global function not // residing in new space. If it is we assume that the function will stay the @@ -5752,7 +6081,7 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { !info()->global_object()->IsAccessCheckNeeded()) { Handle<String> name = proxy->name(); Handle<GlobalObject> global(info()->global_object()); - LookupResult lookup; + LookupResult lookup(isolate()); global->Lookup(*name, &lookup); if (lookup.IsProperty() && lookup.type() == NORMAL && @@ -5827,14 +6156,16 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { } -void HGraphBuilder::VisitCompareToNull(CompareToNull* expr) { +void HGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr, + HValue* value, + NilValue nil) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); - CHECK_ALIVE(VisitForValue(expr->expression())); - HValue* value = Pop(); - HIsNullAndBranch* instr = - new(zone()) HIsNullAndBranch(value, expr->is_strict()); + EqualityKind kind = + expr->op() == Token::EQ_STRICT ? kStrictEquality : kNonStrictEquality; + HIsNilAndBranch* instr = new(zone()) HIsNilAndBranch(value, kind, nil); + instr->set_position(expr->position()); return ast_context()->ReturnControl(instr, expr->id()); } @@ -5843,7 +6174,8 @@ void HGraphBuilder::VisitThisFunction(ThisFunction* expr) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); - HThisFunction* self = new(zone()) HThisFunction; + HThisFunction* self = new(zone()) HThisFunction( + function_state()->compilation_info()->closure()); return ast_context()->ReturnInstruction(self, expr->id()); } @@ -5854,9 +6186,11 @@ void HGraphBuilder::VisitDeclaration(Declaration* decl) { void HGraphBuilder::HandleDeclaration(VariableProxy* proxy, - Variable::Mode mode, + VariableMode mode, FunctionLiteral* function) { - if (mode == Variable::LET) return Bailout("unsupported let declaration"); + if (mode == LET || mode == CONST_HARMONY) { + return Bailout("unsupported harmony declaration"); + } Variable* var = proxy->var(); switch (var->location()) { case Variable::UNALLOCATED: @@ -5864,9 +6198,9 @@ void HGraphBuilder::HandleDeclaration(VariableProxy* proxy, case Variable::PARAMETER: case Variable::LOCAL: case Variable::CONTEXT: - if (mode == Variable::CONST || function != NULL) { + if (mode == CONST || function != NULL) { HValue* value = NULL; - if (mode == Variable::CONST) { + if (mode == CONST) { value = graph()->GetConstantHole(); } else { VisitForValue(function); @@ -5877,7 +6211,7 @@ void HGraphBuilder::HandleDeclaration(VariableProxy* proxy, HStoreContextSlot* store = new HStoreContextSlot(context, var->index(), value); AddInstruction(store); - if (store->HasSideEffects()) AddSimulate(proxy->id()); + if (store->HasObservableSideEffects()) AddSimulate(proxy->id()); } else { environment()->Bind(var, value); } @@ -5917,9 +6251,7 @@ void HGraphBuilder::GenerateIsFunction(CallRuntime* call) { CHECK_ALIVE(VisitForValue(call->arguments()->at(0))); HValue* value = Pop(); HHasInstanceTypeAndBranch* result = - new(zone()) HHasInstanceTypeAndBranch(value, - JS_FUNCTION_TYPE, - JS_FUNCTION_PROXY_TYPE); + new(zone()) HHasInstanceTypeAndBranch(value, JS_FUNCTION_TYPE); return ast_context()->ReturnControl(result, call->id()); } @@ -6047,7 +6379,44 @@ void HGraphBuilder::GenerateValueOf(CallRuntime* call) { void HGraphBuilder::GenerateSetValueOf(CallRuntime* call) { - return Bailout("inlined runtime function: SetValueOf"); + ASSERT(call->arguments()->length() == 2); + CHECK_ALIVE(VisitForValue(call->arguments()->at(0))); + CHECK_ALIVE(VisitForValue(call->arguments()->at(1))); + HValue* value = Pop(); + HValue* object = Pop(); + // Check if object is a not a smi. + HIsSmiAndBranch* smicheck = new(zone()) HIsSmiAndBranch(object); + HBasicBlock* if_smi = graph()->CreateBasicBlock(); + HBasicBlock* if_heap_object = graph()->CreateBasicBlock(); + HBasicBlock* join = graph()->CreateBasicBlock(); + smicheck->SetSuccessorAt(0, if_smi); + smicheck->SetSuccessorAt(1, if_heap_object); + current_block()->Finish(smicheck); + if_smi->Goto(join); + + // Check if object is a JSValue. + set_current_block(if_heap_object); + HHasInstanceTypeAndBranch* typecheck = + new(zone()) HHasInstanceTypeAndBranch(object, JS_VALUE_TYPE); + HBasicBlock* if_js_value = graph()->CreateBasicBlock(); + HBasicBlock* not_js_value = graph()->CreateBasicBlock(); + typecheck->SetSuccessorAt(0, if_js_value); + typecheck->SetSuccessorAt(1, not_js_value); + current_block()->Finish(typecheck); + not_js_value->Goto(join); + + // Create in-object property store to kValueOffset. + set_current_block(if_js_value); + Handle<String> name = isolate()->factory()->undefined_symbol(); + AddInstruction(new HStoreNamedField(object, + name, + value, + true, // in-object store. + JSValue::kValueOffset)); + if_js_value->Goto(join); + join->SetJoinId(call->id()); + set_current_block(join); + return ast_context()->ReturnValue(value); } @@ -6210,12 +6579,37 @@ void HGraphBuilder::GenerateCallFunction(CallRuntime* call) { CHECK_ALIVE(VisitArgument(call->arguments()->at(i))); } CHECK_ALIVE(VisitForValue(call->arguments()->last())); + HValue* function = Pop(); HValue* context = environment()->LookupContext(); - HInvokeFunction* result = - new(zone()) HInvokeFunction(context, function, arg_count); + + // Branch for function proxies, or other non-functions. + HHasInstanceTypeAndBranch* typecheck = + new(zone()) HHasInstanceTypeAndBranch(function, JS_FUNCTION_TYPE); + HBasicBlock* if_jsfunction = graph()->CreateBasicBlock(); + HBasicBlock* if_nonfunction = graph()->CreateBasicBlock(); + HBasicBlock* join = graph()->CreateBasicBlock(); + typecheck->SetSuccessorAt(0, if_jsfunction); + typecheck->SetSuccessorAt(1, if_nonfunction); + current_block()->Finish(typecheck); + + set_current_block(if_jsfunction); + HInstruction* invoke_result = AddInstruction( + new(zone()) HInvokeFunction(context, function, arg_count)); Drop(arg_count); - return ast_context()->ReturnInstruction(result, call->id()); + Push(invoke_result); + if_jsfunction->Goto(join); + + set_current_block(if_nonfunction); + HInstruction* call_result = AddInstruction( + new(zone()) HCallFunction(context, function, arg_count)); + Drop(arg_count); + Push(call_result); + if_nonfunction->Goto(join); + + set_current_block(join); + join->SetJoinId(call->id()); + return ast_context()->ReturnValue(Pop()); } @@ -6255,6 +6649,18 @@ void HGraphBuilder::GenerateMathCos(CallRuntime* call) { } +void HGraphBuilder::GenerateMathTan(CallRuntime* call) { + ASSERT_EQ(1, call->arguments()->length()); + CHECK_ALIVE(VisitArgumentList(call->arguments())); + HValue* context = environment()->LookupContext(); + HCallStub* result = + new(zone()) HCallStub(context, CodeStub::TranscendentalCache, 1); + result->set_transcendental_type(TranscendentalCache::TAN); + Drop(1); + return ast_context()->ReturnInstruction(result, call->id()); +} + + void HGraphBuilder::GenerateMathLog(CallRuntime* call) { ASSERT_EQ(1, call->arguments()->length()); CHECK_ALIVE(VisitArgumentList(call->arguments())); @@ -6472,7 +6878,7 @@ HEnvironment* HEnvironment::CopyForInlining( // If the function we are inlining is a strict mode function or a // builtin function, pass undefined as the receiver for function // calls (instead of the global receiver). - if ((target->shared()->native() || function->strict_mode()) && + if ((target->shared()->native() || !function->is_classic_mode()) && call_kind == CALL_AS_FUNCTION) { inner->SetValueAt(0, undefined); } @@ -6819,7 +7225,7 @@ void HPhase::End() const { } #ifdef DEBUG - if (graph_ != NULL) graph_->Verify(); + if (graph_ != NULL) graph_->Verify(false); // No full verify. if (allocator_ != NULL) allocator_->Verify(); #endif } |