| Index: src/hydrogen.cc
|
| ===================================================================
|
| --- src/hydrogen.cc (revision 9531)
|
| +++ src/hydrogen.cc (working copy)
|
| @@ -422,7 +422,7 @@
|
| };
|
|
|
|
|
| -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 +473,27 @@
|
| // 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()));
|
| + }
|
| }
|
| }
|
| }
|
| @@ -850,7 +852,7 @@
|
| }
|
|
|
|
|
| -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 +865,11 @@
|
| }
|
|
|
|
|
| -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 +880,18 @@
|
| }
|
|
|
|
|
| +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) {
|
| @@ -1848,7 +1860,7 @@
|
| }
|
|
|
| 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);
|
| }
|
|
|
| @@ -2320,17 +2332,24 @@
|
|
|
| graph()->OrderBlocks();
|
| graph()->AssignDominators();
|
| +
|
| +#ifdef DEBUG
|
| + // Do a full verify after building the graph and computing dominators.
|
| + graph()->Verify(true);
|
| +#endif
|
| +
|
| graph()->PropagateDeoptimizingMark();
|
| + if (!graph()->CheckConstPhiUses()) {
|
| + Bailout("Unsupported phi use of const variable");
|
| + return NULL;
|
| + }
|
| graph()->EliminateRedundantPhis();
|
| - if (!graph()->CheckPhis()) {
|
| - Bailout("Unsupported phi use of arguments object");
|
| + if (!graph()->CheckArgumentsPhiUses()) {
|
| + Bailout("Unsupported phi use of arguments");
|
| return NULL;
|
| }
|
| if (FLAG_eliminate_dead_phis) graph()->EliminateUnreachablePhis();
|
| - if (!graph()->CollectPhis()) {
|
| - Bailout("Unsupported phi use of uninitialized constant");
|
| - return NULL;
|
| - }
|
| + graph()->CollectPhis();
|
|
|
| HInferRepresentation rep(graph());
|
| rep.Analyze();
|
| @@ -3127,6 +3146,16 @@
|
| }
|
| switch (variable->location()) {
|
| case Variable::UNALLOCATED: {
|
| + // 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;
|
| GlobalPropertyAccess type =
|
| LookupGlobalProperty(variable, &lookup, false);
|
| @@ -3139,8 +3168,8 @@
|
| 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();
|
| @@ -3317,7 +3346,47 @@
|
| HValue* key = AddInstruction(
|
| new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)),
|
| Representation::Integer32()));
|
| - AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value));
|
| + if (FLAG_smi_only_arrays) {
|
| + 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_SMI_ONLY_ELEMENTS,
|
| + Token::EQ_STRICT);
|
| + smi_elements_check->SetSuccessorAt(0, store_generic);
|
| + smi_elements_check->SetSuccessorAt(1, store_fast_edgesplit2);
|
| + 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);
|
| + } else {
|
| + AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value));
|
| + }
|
| +
|
| AddSimulate(expr->GetIdForElement(i));
|
| }
|
| return ast_context()->ReturnValue(Pop());
|
| @@ -3561,10 +3630,10 @@
|
| LookupResult lookup;
|
| 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);
|
| @@ -3928,6 +3997,7 @@
|
| case EXTERNAL_FLOAT_ELEMENTS:
|
| case EXTERNAL_DOUBLE_ELEMENTS:
|
| break;
|
| + case FAST_SMI_ONLY_ELEMENTS:
|
| case FAST_ELEMENTS:
|
| case FAST_DOUBLE_ELEMENTS:
|
| case DICTIONARY_ELEMENTS:
|
| @@ -3944,6 +4014,30 @@
|
| }
|
|
|
|
|
| +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,
|
| @@ -3951,17 +4045,20 @@
|
| bool is_store) {
|
| ASSERT(expr->IsMonomorphic());
|
| Handle<Map> map = expr->GetMonomorphicReceiverType();
|
| - if (!map->has_fast_elements() &&
|
| - !map->has_fast_double_elements() &&
|
| + AddInstruction(new(zone()) HCheckNonSmi(object));
|
| + 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()));
|
| }
|
| @@ -3976,28 +4073,15 @@
|
| 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);
|
| }
|
|
|
|
|
| @@ -4039,14 +4123,20 @@
|
| 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
|
| @@ -4068,15 +4158,25 @@
|
|
|
| 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 =
|
| @@ -4086,29 +4186,15 @@
|
| 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();
|
| if (position != -1) {
|
| access->set_position(position);
|
| @@ -4118,25 +4204,8 @@
|
| 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));
|
| @@ -4474,20 +4543,25 @@
|
| return false;
|
| }
|
|
|
| - // No context change required.
|
| CompilationInfo* outer_info = info();
|
| +#if !defined(V8_TARGET_ARCH_IA32)
|
| + // Target must be able to use caller's context.
|
| if (target->context() != outer_info->closure()->context() ||
|
| outer_info->scope()->contains_with() ||
|
| outer_info->scope()->num_heap_slots() > 0) {
|
| TraceInline(target, caller, "target requires context change");
|
| return false;
|
| }
|
| +#endif
|
|
|
| +
|
| // Don't inline deeper than kMaxInliningLevels calls.
|
| HEnvironment* env = environment();
|
| int current_level = 1;
|
| while (env->outer() != NULL) {
|
| - if (current_level == Compiler::kMaxInliningLevels) {
|
| + if (current_level == (FLAG_limit_inlining
|
| + ? Compiler::kMaxInliningLevels
|
| + : 2 * Compiler::kMaxInliningLevels)) {
|
| TraceInline(target, caller, "inline depth limit reached");
|
| return false;
|
| }
|
| @@ -4602,6 +4676,17 @@
|
| 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());
|
| @@ -4922,8 +5007,8 @@
|
| }
|
|
|
| } else {
|
| + expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION);
|
| VariableProxy* proxy = expr->expression()->AsVariableProxy();
|
| - // FIXME.
|
| bool global_call = proxy != NULL && proxy->var()->IsUnallocated();
|
|
|
| if (global_call) {
|
| @@ -4975,6 +5060,46 @@
|
| 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)) {
|
| + // The function is lingering in the deoptimization environment.
|
| + // Handle it by case analysis on the AST context.
|
| + if (ast_context()->IsEffect()) {
|
| + Drop(1);
|
| + } else if (ast_context()->IsValue()) {
|
| + HValue* result = Pop();
|
| + Drop(1);
|
| + Push(result);
|
| + } else if (ast_context()->IsTest()) {
|
| + TestContext* context = TestContext::cast(ast_context());
|
| + if (context->if_true()->HasPredecessor()) {
|
| + context->if_true()->last_environment()->Drop(1);
|
| + }
|
| + if (context->if_false()->HasPredecessor()) {
|
| + context->if_true()->last_environment()->Drop(1);
|
| + }
|
| + } else {
|
| + UNREACHABLE();
|
| + }
|
| + return;
|
| + } else {
|
| + call = PreProcessCall(new(zone()) HInvokeFunction(context,
|
| + function,
|
| + argument_count));
|
| + Drop(1); // The function.
|
| + }
|
| +
|
| } else {
|
| CHECK_ALIVE(VisitArgument(expr->expression()));
|
| HValue* context = environment()->LookupContext();
|
| @@ -5668,26 +5793,36 @@
|
| }
|
|
|
|
|
| -void HGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* compare_expr,
|
| - Expression* expr,
|
| +void HGraphBuilder::HandleLiteralCompareTypeof(CompareOperation* expr,
|
| + Expression* sub_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());
|
| + CHECK_ALIVE(VisitForTypeOf(sub_expr));
|
| + HValue* value = Pop();
|
| + 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());
|
| +bool HGraphBuilder::TryLiteralCompare(CompareOperation* expr) {
|
| + Expression *sub_expr;
|
| + Handle<String> check;
|
| + if (expr->IsLiteralCompareTypeof(&sub_expr, &check)) {
|
| + HandleLiteralCompareTypeof(expr, sub_expr, check);
|
| + return true;
|
| + }
|
| +
|
| + if (expr->IsLiteralCompareUndefined(&sub_expr)) {
|
| + HandleLiteralCompareNil(expr, sub_expr, kUndefinedValue);
|
| + return true;
|
| + }
|
| +
|
| + if (expr->IsLiteralCompareNull(&sub_expr)) {
|
| + HandleLiteralCompareNil(expr, sub_expr, kNullValue);
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| }
|
|
|
|
|
| @@ -5709,18 +5844,8 @@
|
| }
|
|
|
| // 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 (TryLiteralCompare(expr)) 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.
|
| if (type_info.IsUninitialized()) {
|
| @@ -5824,14 +5949,18 @@
|
| }
|
|
|
|
|
| -void HGraphBuilder::VisitCompareToNull(CompareToNull* expr) {
|
| +void HGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr,
|
| + Expression* sub_expr,
|
| + NilValue nil) {
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| - CHECK_ALIVE(VisitForValue(expr->expression()));
|
| + CHECK_ALIVE(VisitForValue(sub_expr));
|
| 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());
|
| }
|
|
|
| @@ -5914,9 +6043,7 @@
|
| 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());
|
| }
|
|
|
| @@ -6816,7 +6943,7 @@
|
| }
|
|
|
| #ifdef DEBUG
|
| - if (graph_ != NULL) graph_->Verify();
|
| + if (graph_ != NULL) graph_->Verify(false); // No full verify.
|
| if (allocator_ != NULL) allocator_->Verify();
|
| #endif
|
| }
|
|
|