| Index: src/hydrogen.cc
|
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc
|
| index 54e7ee82b93e747a1089cf6d4220a41696a38cc0..55d19e05b264910d35bbc57202d55e7e46be7aa2 100644
|
| --- a/src/hydrogen.cc
|
| +++ b/src/hydrogen.cc
|
| @@ -68,6 +68,8 @@
|
| #include "ia32/lithium-codegen-ia32.h"
|
| #elif V8_TARGET_ARCH_X64
|
| #include "x64/lithium-codegen-x64.h"
|
| +#elif V8_TARGET_ARCH_A64
|
| +#include "a64/lithium-codegen-a64.h"
|
| #elif V8_TARGET_ARCH_ARM
|
| #include "arm/lithium-codegen-arm.h"
|
| #elif V8_TARGET_ARCH_MIPS
|
| @@ -141,12 +143,13 @@ void HBasicBlock::RemovePhi(HPhi* phi) {
|
| }
|
|
|
|
|
| -void HBasicBlock::AddInstruction(HInstruction* instr, int position) {
|
| +void HBasicBlock::AddInstruction(HInstruction* instr,
|
| + HSourcePosition position) {
|
| ASSERT(!IsStartBlock() || !IsFinished());
|
| ASSERT(!instr->IsLinked());
|
| ASSERT(!IsFinished());
|
|
|
| - if (position != RelocInfo::kNoPosition) {
|
| + if (!position.IsUnknown()) {
|
| instr->set_position(position);
|
| }
|
| if (first_ == NULL) {
|
| @@ -154,10 +157,10 @@ void HBasicBlock::AddInstruction(HInstruction* instr, int position) {
|
| ASSERT(!last_environment()->ast_id().IsNone());
|
| HBlockEntry* entry = new(zone()) HBlockEntry();
|
| entry->InitializeAsFirst(this);
|
| - if (position != RelocInfo::kNoPosition) {
|
| + if (!position.IsUnknown()) {
|
| entry->set_position(position);
|
| } else {
|
| - ASSERT(!FLAG_emit_opt_code_positions ||
|
| + ASSERT(!FLAG_hydrogen_track_positions ||
|
| !graph()->info()->IsOptimizing());
|
| }
|
| first_ = last_ = entry;
|
| @@ -210,7 +213,7 @@ HSimulate* HBasicBlock::CreateSimulate(BailoutId ast_id,
|
| }
|
|
|
|
|
| -void HBasicBlock::Finish(HControlInstruction* end, int position) {
|
| +void HBasicBlock::Finish(HControlInstruction* end, HSourcePosition position) {
|
| ASSERT(!IsFinished());
|
| AddInstruction(end, position);
|
| end_ = end;
|
| @@ -221,11 +224,11 @@ void HBasicBlock::Finish(HControlInstruction* end, int position) {
|
|
|
|
|
| void HBasicBlock::Goto(HBasicBlock* block,
|
| - int position,
|
| + HSourcePosition position,
|
| FunctionState* state,
|
| bool add_simulate) {
|
| bool drop_extra = state != NULL &&
|
| - state->inlining_kind() == DROP_EXTRA_ON_RETURN;
|
| + state->inlining_kind() == NORMAL_RETURN;
|
|
|
| if (block->IsInlineReturnTarget()) {
|
| HEnvironment* env = last_environment();
|
| @@ -244,9 +247,9 @@ void HBasicBlock::Goto(HBasicBlock* block,
|
|
|
| void HBasicBlock::AddLeaveInlined(HValue* return_value,
|
| FunctionState* state,
|
| - int position) {
|
| + HSourcePosition position) {
|
| HBasicBlock* target = state->function_return();
|
| - bool drop_extra = state->inlining_kind() == DROP_EXTRA_ON_RETURN;
|
| + bool drop_extra = state->inlining_kind() == NORMAL_RETURN;
|
|
|
| ASSERT(target->IsInlineReturnTarget());
|
| ASSERT(return_value != NULL);
|
| @@ -302,6 +305,12 @@ bool HBasicBlock::Dominates(HBasicBlock* other) const {
|
| }
|
|
|
|
|
| +bool HBasicBlock::EqualToOrDominates(HBasicBlock* other) const {
|
| + if (this == other) return true;
|
| + return Dominates(other);
|
| +}
|
| +
|
| +
|
| int HBasicBlock::LoopNestingDepth() const {
|
| const HBasicBlock* current = this;
|
| int result = (current->IsLoopHeader()) ? 1 : 0;
|
| @@ -331,6 +340,15 @@ void HBasicBlock::PostProcessLoopHeader(IterationStatement* stmt) {
|
| }
|
|
|
|
|
| +void HBasicBlock::MarkSuccEdgeUnreachable(int succ) {
|
| + ASSERT(IsFinished());
|
| + HBasicBlock* succ_block = end()->SuccessorAt(succ);
|
| +
|
| + ASSERT(succ_block->predecessors()->length() == 1);
|
| + succ_block->MarkUnreachable();
|
| +}
|
| +
|
| +
|
| void HBasicBlock::RegisterPredecessor(HBasicBlock* pred) {
|
| if (HasPredecessor()) {
|
| // Only loop header blocks can have a predecessor added after
|
| @@ -1026,9 +1044,9 @@ void HGraphBuilder::IfBuilder::End() {
|
| current = merge_at_join_blocks_;
|
| while (current != NULL) {
|
| if (current->deopt_ && current->block_ != NULL) {
|
| - builder_->PadEnvironmentForContinuation(current->block_,
|
| - merge_block);
|
| - builder_->GotoNoSimulate(current->block_, merge_block);
|
| + current->block_->FinishExit(
|
| + HAbnormalExit::New(builder_->zone(), NULL),
|
| + HSourcePosition::Unknown());
|
| }
|
| current = current->next_;
|
| }
|
| @@ -1161,9 +1179,10 @@ HGraph* HGraphBuilder::CreateGraph() {
|
|
|
| HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) {
|
| ASSERT(current_block() != NULL);
|
| - ASSERT(!FLAG_emit_opt_code_positions ||
|
| - position_ != RelocInfo::kNoPosition || !info_->IsOptimizing());
|
| - current_block()->AddInstruction(instr, position_);
|
| + ASSERT(!FLAG_hydrogen_track_positions ||
|
| + !position_.IsUnknown() ||
|
| + !info_->IsOptimizing());
|
| + current_block()->AddInstruction(instr, source_position());
|
| if (graph()->IsInsideNoSideEffectsScope()) {
|
| instr->SetFlag(HValue::kHasNoObservableSideEffects);
|
| }
|
| @@ -1172,9 +1191,10 @@ HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) {
|
|
|
|
|
| void HGraphBuilder::FinishCurrentBlock(HControlInstruction* last) {
|
| - ASSERT(!FLAG_emit_opt_code_positions || !info_->IsOptimizing() ||
|
| - position_ != RelocInfo::kNoPosition);
|
| - current_block()->Finish(last, position_);
|
| + ASSERT(!FLAG_hydrogen_track_positions ||
|
| + !info_->IsOptimizing() ||
|
| + !position_.IsUnknown());
|
| + current_block()->Finish(last, source_position());
|
| if (last->IsReturn() || last->IsAbnormalExit()) {
|
| set_current_block(NULL);
|
| }
|
| @@ -1182,9 +1202,9 @@ void HGraphBuilder::FinishCurrentBlock(HControlInstruction* last) {
|
|
|
|
|
| void HGraphBuilder::FinishExitCurrentBlock(HControlInstruction* instruction) {
|
| - ASSERT(!FLAG_emit_opt_code_positions || !info_->IsOptimizing() ||
|
| - position_ != RelocInfo::kNoPosition);
|
| - current_block()->FinishExit(instruction, position_);
|
| + ASSERT(!FLAG_hydrogen_track_positions || !info_->IsOptimizing() ||
|
| + !position_.IsUnknown());
|
| + current_block()->FinishExit(instruction, source_position());
|
| if (instruction->IsReturn() || instruction->IsAbnormalExit()) {
|
| set_current_block(NULL);
|
| }
|
| @@ -1194,12 +1214,12 @@ void HGraphBuilder::FinishExitCurrentBlock(HControlInstruction* instruction) {
|
| void HGraphBuilder::AddIncrementCounter(StatsCounter* counter) {
|
| if (FLAG_native_code_counters && counter->Enabled()) {
|
| HValue* reference = Add<HConstant>(ExternalReference(counter));
|
| - HValue* old_value = Add<HLoadNamedField>(reference,
|
| - HObjectAccess::ForCounter());
|
| + HValue* old_value = Add<HLoadNamedField>(
|
| + reference, static_cast<HValue*>(NULL), HObjectAccess::ForCounter());
|
| HValue* new_value = AddUncasted<HAdd>(old_value, graph()->GetConstant1());
|
| new_value->ClearFlag(HValue::kCanOverflow); // Ignore counter overflow
|
| Add<HStoreNamedField>(reference, HObjectAccess::ForCounter(),
|
| - new_value);
|
| + new_value, STORE_TO_INITIALIZED_ENTRY);
|
| }
|
| }
|
|
|
| @@ -1208,7 +1228,7 @@ void HGraphBuilder::AddSimulate(BailoutId id,
|
| RemovableSimulate removable) {
|
| ASSERT(current_block() != NULL);
|
| ASSERT(!graph()->IsInsideNoSideEffectsScope());
|
| - current_block()->AddNewSimulate(id, removable);
|
| + current_block()->AddNewSimulate(id, source_position(), removable);
|
| }
|
|
|
|
|
| @@ -1234,38 +1254,9 @@ HValue* HGraphBuilder::BuildCheckHeapObject(HValue* obj) {
|
| }
|
|
|
|
|
| -void HGraphBuilder::FinishExitWithHardDeoptimization(
|
| - const char* reason, HBasicBlock* continuation) {
|
| - PadEnvironmentForContinuation(current_block(), continuation);
|
| +void HGraphBuilder::FinishExitWithHardDeoptimization(const char* reason) {
|
| Add<HDeoptimize>(reason, Deoptimizer::EAGER);
|
| - if (graph()->IsInsideNoSideEffectsScope()) {
|
| - GotoNoSimulate(continuation);
|
| - } else {
|
| - Goto(continuation);
|
| - }
|
| -}
|
| -
|
| -
|
| -void HGraphBuilder::PadEnvironmentForContinuation(
|
| - HBasicBlock* from,
|
| - HBasicBlock* continuation) {
|
| - if (continuation->last_environment() != NULL) {
|
| - // When merging from a deopt block to a continuation, resolve differences in
|
| - // environment by pushing constant 0 and popping extra values so that the
|
| - // environments match during the join. Push 0 since it has the most specific
|
| - // representation, and will not influence representation inference of the
|
| - // phi.
|
| - int continuation_env_length = continuation->last_environment()->length();
|
| - while (continuation_env_length != from->last_environment()->length()) {
|
| - if (continuation_env_length > from->last_environment()->length()) {
|
| - from->last_environment()->Push(graph()->GetConstant0());
|
| - } else {
|
| - from->last_environment()->Pop();
|
| - }
|
| - }
|
| - } else {
|
| - ASSERT(continuation->predecessors()->length() == 0);
|
| - }
|
| + FinishExitCurrentBlock(New<HAbnormalExit>());
|
| }
|
|
|
|
|
| @@ -1287,17 +1278,25 @@ HValue* HGraphBuilder::BuildCheckString(HValue* string) {
|
|
|
| HValue* HGraphBuilder::BuildWrapReceiver(HValue* object, HValue* function) {
|
| if (object->type().IsJSObject()) return object;
|
| + if (function->IsConstant() &&
|
| + HConstant::cast(function)->handle(isolate())->IsJSFunction()) {
|
| + Handle<JSFunction> f = Handle<JSFunction>::cast(
|
| + HConstant::cast(function)->handle(isolate()));
|
| + SharedFunctionInfo* shared = f->shared();
|
| + if (!shared->is_classic_mode() || shared->native()) return object;
|
| + }
|
| return Add<HWrapReceiver>(object, function);
|
| }
|
|
|
|
|
| -HValue* HGraphBuilder::BuildCheckForCapacityGrow(HValue* object,
|
| - HValue* elements,
|
| - ElementsKind kind,
|
| - HValue* length,
|
| - HValue* key,
|
| - bool is_js_array,
|
| - bool is_store) {
|
| +HValue* HGraphBuilder::BuildCheckForCapacityGrow(
|
| + HValue* object,
|
| + HValue* elements,
|
| + ElementsKind kind,
|
| + HValue* length,
|
| + HValue* key,
|
| + bool is_js_array,
|
| + PropertyAccessType access_type) {
|
| IfBuilder length_checker(this);
|
|
|
| Token::Value token = IsHoleyElementsKind(kind) ? Token::GTE : Token::EQ;
|
| @@ -1340,7 +1339,7 @@ HValue* HGraphBuilder::BuildCheckForCapacityGrow(HValue* object,
|
| new_length);
|
| }
|
|
|
| - if (is_store && kind == FAST_SMI_ELEMENTS) {
|
| + if (access_type == STORE && kind == FAST_SMI_ELEMENTS) {
|
| HValue* checked_elements = environment()->Top();
|
|
|
| // Write zero to ensure that the new element is initialized with some smi.
|
| @@ -1412,7 +1411,8 @@ void HGraphBuilder::BuildTransitionElementsKind(HValue* object,
|
| HInstruction* elements_length = AddLoadFixedArrayLength(elements);
|
|
|
| HInstruction* array_length = is_jsarray
|
| - ? Add<HLoadNamedField>(object, HObjectAccess::ForArrayLength(from_kind))
|
| + ? Add<HLoadNamedField>(object, static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForArrayLength(from_kind))
|
| : elements_length;
|
|
|
| BuildGrowElementsCapacity(object, elements, from_kind, to_kind,
|
| @@ -1450,7 +1450,7 @@ HValue* HGraphBuilder::BuildUncheckedDictionaryElementLoadHelper(
|
|
|
| HValue* candidate_key = Add<HLoadKeyed>(elements, key_index,
|
| static_cast<HValue*>(NULL),
|
| - FAST_SMI_ELEMENTS);
|
| + FAST_ELEMENTS);
|
|
|
| IfBuilder key_compare(this);
|
| key_compare.IfNot<HCompareObjectEqAndBranch>(key, candidate_key);
|
| @@ -1476,7 +1476,7 @@ HValue* HGraphBuilder::BuildUncheckedDictionaryElementLoadHelper(
|
|
|
| HValue* details = Add<HLoadKeyed>(elements, details_index,
|
| static_cast<HValue*>(NULL),
|
| - FAST_SMI_ELEMENTS);
|
| + FAST_ELEMENTS);
|
| IfBuilder details_compare(this);
|
| details_compare.If<HCompareNumericAndBranch>(details,
|
| graph()->GetConstant0(),
|
| @@ -1546,7 +1546,7 @@ HValue* HGraphBuilder::BuildUncheckedDictionaryElementLoad(HValue* receiver,
|
| elements,
|
| Add<HConstant>(NameDictionary::kCapacityIndex),
|
| static_cast<HValue*>(NULL),
|
| - FAST_SMI_ELEMENTS);
|
| + FAST_ELEMENTS);
|
|
|
| HValue* mask = AddUncasted<HSub>(capacity, graph()->GetConstant1());
|
| mask->ChangeRepresentation(Representation::Integer32());
|
| @@ -1557,8 +1557,74 @@ HValue* HGraphBuilder::BuildUncheckedDictionaryElementLoad(HValue* receiver,
|
| }
|
|
|
|
|
| -HValue* HGraphBuilder::BuildNumberToString(HValue* object,
|
| - Handle<Type> type) {
|
| +HValue* HGraphBuilder::BuildRegExpConstructResult(HValue* length,
|
| + HValue* index,
|
| + HValue* input) {
|
| + NoObservableSideEffectsScope scope(this);
|
| +
|
| + // Compute the size of the RegExpResult followed by FixedArray with length.
|
| + HValue* size = length;
|
| + size = AddUncasted<HShl>(size, Add<HConstant>(kPointerSizeLog2));
|
| + size = AddUncasted<HAdd>(size, Add<HConstant>(static_cast<int32_t>(
|
| + JSRegExpResult::kSize + FixedArray::kHeaderSize)));
|
| +
|
| + // Make sure size does not exceeds max regular heap object size.
|
| + Add<HBoundsCheck>(size, Add<HConstant>(Page::kMaxRegularHeapObjectSize));
|
| +
|
| + // Allocate the JSRegExpResult and the FixedArray in one step.
|
| + HValue* result = Add<HAllocate>(
|
| + size, HType::JSArray(), NOT_TENURED, JS_ARRAY_TYPE);
|
| +
|
| + // Determine the elements FixedArray.
|
| + HValue* elements = Add<HInnerAllocatedObject>(
|
| + result, Add<HConstant>(JSRegExpResult::kSize));
|
| +
|
| + // Initialize the JSRegExpResult header.
|
| + HValue* global_object = Add<HLoadNamedField>(
|
| + context(), static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForContextSlot(Context::GLOBAL_OBJECT_INDEX));
|
| + HValue* native_context = Add<HLoadNamedField>(
|
| + global_object, static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForGlobalObjectNativeContext());
|
| + AddStoreMapNoWriteBarrier(result, Add<HLoadNamedField>(
|
| + native_context, static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForContextSlot(Context::REGEXP_RESULT_MAP_INDEX)));
|
| + Add<HStoreNamedField>(
|
| + result, HObjectAccess::ForJSArrayOffset(JSArray::kPropertiesOffset),
|
| + Add<HConstant>(isolate()->factory()->empty_fixed_array()));
|
| + Add<HStoreNamedField>(
|
| + result, HObjectAccess::ForJSArrayOffset(JSArray::kElementsOffset),
|
| + elements);
|
| + Add<HStoreNamedField>(
|
| + result, HObjectAccess::ForJSArrayOffset(JSArray::kLengthOffset), length);
|
| +
|
| + // Initialize the additional fields.
|
| + Add<HStoreNamedField>(
|
| + result, HObjectAccess::ForJSArrayOffset(JSRegExpResult::kIndexOffset),
|
| + index);
|
| + Add<HStoreNamedField>(
|
| + result, HObjectAccess::ForJSArrayOffset(JSRegExpResult::kInputOffset),
|
| + input);
|
| +
|
| + // Initialize the elements header.
|
| + AddStoreMapConstantNoWriteBarrier(elements,
|
| + isolate()->factory()->fixed_array_map());
|
| + Add<HStoreNamedField>(elements, HObjectAccess::ForFixedArrayLength(), length);
|
| +
|
| + // Initialize the elements contents with undefined.
|
| + LoopBuilder loop(this, context(), LoopBuilder::kPostIncrement);
|
| + index = loop.BeginBody(graph()->GetConstant0(), length, Token::LT);
|
| + {
|
| + Add<HStoreKeyed>(elements, index, graph()->GetConstantUndefined(),
|
| + FAST_ELEMENTS);
|
| + }
|
| + loop.EndBody();
|
| +
|
| + return result;
|
| +}
|
| +
|
| +
|
| +HValue* HGraphBuilder::BuildNumberToString(HValue* object, Type* type) {
|
| NoObservableSideEffectsScope scope(this);
|
|
|
| // Convert constant numbers at compile time.
|
| @@ -1614,15 +1680,17 @@ HValue* HGraphBuilder::BuildNumberToString(HValue* object,
|
| } else {
|
| // Check if the object is a heap number.
|
| IfBuilder if_objectisnumber(this);
|
| - if_objectisnumber.If<HCompareMap>(
|
| + HValue* objectisnumber = if_objectisnumber.If<HCompareMap>(
|
| object, isolate()->factory()->heap_number_map());
|
| if_objectisnumber.Then();
|
| {
|
| // Compute hash for heap number similar to double_get_hash().
|
| HValue* low = Add<HLoadNamedField>(
|
| - object, HObjectAccess::ForHeapNumberValueLowestBits());
|
| + object, objectisnumber,
|
| + HObjectAccess::ForHeapNumberValueLowestBits());
|
| HValue* high = Add<HLoadNamedField>(
|
| - object, HObjectAccess::ForHeapNumberValueHighestBits());
|
| + object, objectisnumber,
|
| + HObjectAccess::ForHeapNumberValueHighestBits());
|
| HValue* hash = AddUncasted<HBitwise>(Token::BIT_XOR, low, high);
|
| hash = AddUncasted<HBitwise>(Token::BIT_AND, hash, mask);
|
|
|
| @@ -1635,14 +1703,16 @@ HValue* HGraphBuilder::BuildNumberToString(HValue* object,
|
| // Check if key is a heap number (the number string cache contains only
|
| // SMIs and heap number, so it is sufficient to do a SMI check here).
|
| IfBuilder if_keyisnotsmi(this);
|
| - if_keyisnotsmi.IfNot<HIsSmiAndBranch>(key);
|
| + HValue* keyisnotsmi = if_keyisnotsmi.IfNot<HIsSmiAndBranch>(key);
|
| if_keyisnotsmi.Then();
|
| {
|
| // Check if values of key and object match.
|
| IfBuilder if_keyeqobject(this);
|
| if_keyeqobject.If<HCompareNumericAndBranch>(
|
| - Add<HLoadNamedField>(key, HObjectAccess::ForHeapNumberValue()),
|
| - Add<HLoadNamedField>(object, HObjectAccess::ForHeapNumberValue()),
|
| + Add<HLoadNamedField>(key, keyisnotsmi,
|
| + HObjectAccess::ForHeapNumberValue()),
|
| + Add<HLoadNamedField>(object, objectisnumber,
|
| + HObjectAccess::ForHeapNumberValue()),
|
| Token::EQ);
|
| if_keyeqobject.Then();
|
| {
|
| @@ -2075,7 +2145,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
|
| HValue* val,
|
| bool is_js_array,
|
| ElementsKind elements_kind,
|
| - bool is_store,
|
| + PropertyAccessType access_type,
|
| LoadKeyedHoleMode load_mode,
|
| KeyedAccessStoreMode store_mode) {
|
| ASSERT((!IsExternalArrayElementsKind(elements_kind) &&
|
| @@ -2088,23 +2158,24 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
|
| // for FAST_ELEMENTS, since a transition to HOLEY elements won't change the
|
| // generated store code.
|
| if ((elements_kind == FAST_HOLEY_ELEMENTS) ||
|
| - (elements_kind == FAST_ELEMENTS && is_store)) {
|
| - checked_object->ClearGVNFlag(kDependsOnElementsKind);
|
| + (elements_kind == FAST_ELEMENTS && access_type == STORE)) {
|
| + checked_object->ClearDependsOnFlag(kElementsKind);
|
| }
|
|
|
| bool fast_smi_only_elements = IsFastSmiElementsKind(elements_kind);
|
| bool fast_elements = IsFastObjectElementsKind(elements_kind);
|
| HValue* elements = AddLoadElements(checked_object);
|
| - if (is_store && (fast_elements || fast_smi_only_elements) &&
|
| + if (access_type == STORE && (fast_elements || fast_smi_only_elements) &&
|
| store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
|
| HCheckMaps* check_cow_map = Add<HCheckMaps>(
|
| elements, isolate()->factory()->fixed_array_map(), top_info());
|
| - check_cow_map->ClearGVNFlag(kDependsOnElementsKind);
|
| + check_cow_map->ClearDependsOnFlag(kElementsKind);
|
| }
|
| HInstruction* length = NULL;
|
| if (is_js_array) {
|
| length = Add<HLoadNamedField>(
|
| - checked_object, HObjectAccess::ForArrayLength(elements_kind));
|
| + checked_object, static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForArrayLength(elements_kind));
|
| } else {
|
| length = AddLoadFixedArrayLength(elements);
|
| }
|
| @@ -2114,8 +2185,9 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
|
| IsFixedTypedArrayElementsKind(elements_kind)) {
|
| HValue* backing_store;
|
| if (IsExternalArrayElementsKind(elements_kind)) {
|
| - backing_store =
|
| - Add<HLoadExternalArrayPointer>(elements);
|
| + backing_store = Add<HLoadNamedField>(
|
| + elements, static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForExternalArrayExternalPointer());
|
| } else {
|
| backing_store = elements;
|
| }
|
| @@ -2129,7 +2201,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
|
| key, graph()->GetConstant0(), Token::GTE);
|
| negative_checker.Then();
|
| HInstruction* result = AddElementAccess(
|
| - backing_store, key, val, bounds_check, elements_kind, is_store);
|
| + backing_store, key, val, bounds_check, elements_kind, access_type);
|
| negative_checker.ElseDeopt("Negative key encountered");
|
| negative_checker.End();
|
| length_checker.End();
|
| @@ -2139,7 +2211,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
|
| checked_key = Add<HBoundsCheck>(key, length);
|
| return AddElementAccess(
|
| backing_store, checked_key, val,
|
| - checked_object, elements_kind, is_store);
|
| + checked_object, elements_kind, access_type);
|
| }
|
| }
|
| ASSERT(fast_smi_only_elements ||
|
| @@ -2149,7 +2221,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
|
| // In case val is stored into a fast smi array, assure that the value is a smi
|
| // before manipulating the backing store. Otherwise the actual store may
|
| // deopt, leaving the backing store in an invalid state.
|
| - if (is_store && IsFastSmiElementsKind(elements_kind) &&
|
| + if (access_type == STORE && IsFastSmiElementsKind(elements_kind) &&
|
| !val->type().IsSmi()) {
|
| val = AddUncasted<HForceRepresentation>(val, Representation::Smi());
|
| }
|
| @@ -2158,12 +2230,12 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
|
| NoObservableSideEffectsScope no_effects(this);
|
| elements = BuildCheckForCapacityGrow(checked_object, elements,
|
| elements_kind, length, key,
|
| - is_js_array, is_store);
|
| + is_js_array, access_type);
|
| checked_key = key;
|
| } else {
|
| checked_key = Add<HBoundsCheck>(key, length);
|
|
|
| - if (is_store && (fast_elements || fast_smi_only_elements)) {
|
| + if (access_type == STORE && (fast_elements || fast_smi_only_elements)) {
|
| if (store_mode == STORE_NO_TRANSITION_HANDLE_COW) {
|
| NoObservableSideEffectsScope no_effects(this);
|
| elements = BuildCopyElementsOnWrite(checked_object, elements,
|
| @@ -2171,12 +2243,12 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
|
| } else {
|
| HCheckMaps* check_cow_map = Add<HCheckMaps>(
|
| elements, isolate()->factory()->fixed_array_map(), top_info());
|
| - check_cow_map->ClearGVNFlag(kDependsOnElementsKind);
|
| + check_cow_map->ClearDependsOnFlag(kElementsKind);
|
| }
|
| }
|
| }
|
| return AddElementAccess(elements, checked_key, val, checked_object,
|
| - elements_kind, is_store, load_mode);
|
| + elements_kind, access_type, load_mode);
|
| }
|
|
|
|
|
| @@ -2244,8 +2316,11 @@ HValue* HGraphBuilder::BuildAllocateElements(ElementsKind kind,
|
| HValue* total_size = AddUncasted<HAdd>(mul, header_size);
|
| total_size->ClearFlag(HValue::kCanOverflow);
|
|
|
| - return Add<HAllocate>(total_size, HType::JSArray(),
|
| - isolate()->heap()->GetPretenureMode(), instance_type);
|
| + PretenureFlag pretenure_flag = !FLAG_allocation_site_pretenuring ?
|
| + isolate()->heap()->GetPretenureMode() : NOT_TENURED;
|
| +
|
| + return Add<HAllocate>(total_size, HType::JSArray(), pretenure_flag,
|
| + instance_type);
|
| }
|
|
|
|
|
| @@ -2315,11 +2390,11 @@ HInstruction* HGraphBuilder::AddElementAccess(
|
| HValue* val,
|
| HValue* dependency,
|
| ElementsKind elements_kind,
|
| - bool is_store,
|
| + PropertyAccessType access_type,
|
| LoadKeyedHoleMode load_mode) {
|
| - if (is_store) {
|
| + if (access_type == STORE) {
|
| ASSERT(val != NULL);
|
| - if (elements_kind == EXTERNAL_PIXEL_ELEMENTS ||
|
| + if (elements_kind == EXTERNAL_UINT8_CLAMPED_ELEMENTS ||
|
| elements_kind == UINT8_CLAMPED_ELEMENTS) {
|
| val = Add<HClampToUint8>(val);
|
| }
|
| @@ -2329,12 +2404,12 @@ HInstruction* HGraphBuilder::AddElementAccess(
|
| : INITIALIZING_STORE);
|
| }
|
|
|
| - ASSERT(!is_store);
|
| + ASSERT(access_type == LOAD);
|
| ASSERT(val == NULL);
|
| HLoadKeyed* load = Add<HLoadKeyed>(
|
| elements, checked_key, dependency, elements_kind, load_mode);
|
| if (FLAG_opt_safe_uint32_operations &&
|
| - (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS ||
|
| + (elements_kind == EXTERNAL_UINT32_ELEMENTS ||
|
| elements_kind == UINT32_ELEMENTS)) {
|
| graph()->RecordUint32Instruction(load);
|
| }
|
| @@ -2343,13 +2418,14 @@ HInstruction* HGraphBuilder::AddElementAccess(
|
|
|
|
|
| HLoadNamedField* HGraphBuilder::AddLoadElements(HValue* object) {
|
| - return Add<HLoadNamedField>(object, HObjectAccess::ForElementsPointer());
|
| + return Add<HLoadNamedField>(
|
| + object, static_cast<HValue*>(NULL), HObjectAccess::ForElementsPointer());
|
| }
|
|
|
|
|
| HLoadNamedField* HGraphBuilder::AddLoadFixedArrayLength(HValue* object) {
|
| - return Add<HLoadNamedField>(object,
|
| - HObjectAccess::ForFixedArrayLength());
|
| + return Add<HLoadNamedField>(
|
| + object, static_cast<HValue*>(NULL), HObjectAccess::ForFixedArrayLength());
|
| }
|
|
|
|
|
| @@ -2370,10 +2446,9 @@ HValue* HGraphBuilder::BuildNewElementsCapacity(HValue* old_capacity) {
|
|
|
|
|
| void HGraphBuilder::BuildNewSpaceArrayCheck(HValue* length, ElementsKind kind) {
|
| - Heap* heap = isolate()->heap();
|
| int element_size = IsFastDoubleElementsKind(kind) ? kDoubleSize
|
| : kPointerSize;
|
| - int max_size = heap->MaxRegularSpaceAllocationSize() / element_size;
|
| + int max_size = Page::kMaxRegularHeapObjectSize / element_size;
|
| max_size -= JSArray::kSize / element_size;
|
| HConstant* max_size_constant = Add<HConstant>(max_size);
|
| Add<HBoundsCheck>(length, max_size_constant);
|
| @@ -2533,8 +2608,9 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HValue* boilerplate,
|
| for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
|
| if ((i != JSArray::kElementsOffset) || (length == 0)) {
|
| HObjectAccess access = HObjectAccess::ForJSArrayOffset(i);
|
| - Add<HStoreNamedField>(object, access,
|
| - Add<HLoadNamedField>(boilerplate, access));
|
| + Add<HStoreNamedField>(
|
| + object, access, Add<HLoadNamedField>(
|
| + boilerplate, static_cast<HValue*>(NULL), access));
|
| }
|
| }
|
|
|
| @@ -2562,8 +2638,9 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HValue* boilerplate,
|
| // Copy the elements array header.
|
| for (int i = 0; i < FixedArrayBase::kHeaderSize; i += kPointerSize) {
|
| HObjectAccess access = HObjectAccess::ForFixedArrayHeader(i);
|
| - Add<HStoreNamedField>(object_elements, access,
|
| - Add<HLoadNamedField>(boilerplate_elements, access));
|
| + Add<HStoreNamedField>(
|
| + object_elements, access, Add<HLoadNamedField>(
|
| + boilerplate_elements, static_cast<HValue*>(NULL), access));
|
| }
|
|
|
| // Copy the elements array contents.
|
| @@ -2584,7 +2661,7 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HValue* boilerplate,
|
|
|
| void HGraphBuilder::BuildCompareNil(
|
| HValue* value,
|
| - Handle<Type> type,
|
| + Type* type,
|
| HIfContinuation* continuation) {
|
| IfBuilder if_nil(this);
|
| bool some_case_handled = false;
|
| @@ -2649,7 +2726,8 @@ void HGraphBuilder::BuildCreateAllocationMemento(
|
| allocation_site);
|
| if (FLAG_allocation_site_pretenuring) {
|
| HValue* memento_create_count = Add<HLoadNamedField>(
|
| - allocation_site, HObjectAccess::ForAllocationSiteOffset(
|
| + allocation_site, static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForAllocationSiteOffset(
|
| AllocationSite::kPretenureCreateCountOffset));
|
| memento_create_count = AddUncasted<HAdd>(
|
| memento_create_count, graph()->GetConstant1());
|
| @@ -2668,21 +2746,27 @@ void HGraphBuilder::BuildCreateAllocationMemento(
|
| HInstruction* HGraphBuilder::BuildGetNativeContext(HValue* closure) {
|
| // Get the global context, then the native context
|
| HInstruction* context =
|
| - Add<HLoadNamedField>(closure, HObjectAccess::ForFunctionContextPointer());
|
| - HInstruction* global_object = Add<HLoadNamedField>(context,
|
| + Add<HLoadNamedField>(closure, static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForFunctionContextPointer());
|
| + HInstruction* global_object = Add<HLoadNamedField>(
|
| + context, static_cast<HValue*>(NULL),
|
| HObjectAccess::ForContextSlot(Context::GLOBAL_OBJECT_INDEX));
|
| - HObjectAccess access = HObjectAccess::ForJSObjectOffset(
|
| + HObjectAccess access = HObjectAccess::ForObservableJSObjectOffset(
|
| GlobalObject::kNativeContextOffset);
|
| - return Add<HLoadNamedField>(global_object, access);
|
| + return Add<HLoadNamedField>(
|
| + global_object, static_cast<HValue*>(NULL), access);
|
| }
|
|
|
|
|
| HInstruction* HGraphBuilder::BuildGetNativeContext() {
|
| // Get the global context, then the native context
|
| - HInstruction* global_object = Add<HGlobalObject>();
|
| - HObjectAccess access = HObjectAccess::ForJSObjectOffset(
|
| - GlobalObject::kNativeContextOffset);
|
| - return Add<HLoadNamedField>(global_object, access);
|
| + HValue* global_object = Add<HLoadNamedField>(
|
| + context(), static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForContextSlot(Context::GLOBAL_OBJECT_INDEX));
|
| + return Add<HLoadNamedField>(
|
| + global_object, static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForObservableJSObjectOffset(
|
| + GlobalObject::kNativeContextOffset));
|
| }
|
|
|
|
|
| @@ -2736,7 +2820,8 @@ HValue* HGraphBuilder::JSArrayBuilder::EmitMapCode() {
|
| // No need for a context lookup if the kind_ matches the initial
|
| // map, because we can just load the map in that case.
|
| HObjectAccess access = HObjectAccess::ForPrototypeOrInitialMap();
|
| - return builder()->AddLoadNamedField(constructor_function_, access);
|
| + return builder()->Add<HLoadNamedField>(
|
| + constructor_function_, static_cast<HValue*>(NULL), access);
|
| }
|
|
|
| // TODO(mvstanton): we should always have a constructor function if we
|
| @@ -2761,7 +2846,8 @@ HValue* HGraphBuilder::JSArrayBuilder::EmitMapCode() {
|
| HValue* HGraphBuilder::JSArrayBuilder::EmitInternalMapCode() {
|
| // Find the map near the constructor function
|
| HObjectAccess access = HObjectAccess::ForPrototypeOrInitialMap();
|
| - return builder()->AddLoadNamedField(constructor_function_, access);
|
| + return builder()->Add<HLoadNamedField>(
|
| + constructor_function_, static_cast<HValue*>(NULL), access);
|
| }
|
|
|
|
|
| @@ -2878,20 +2964,24 @@ HStoreNamedField* HGraphBuilder::AddStoreMapConstant(HValue *object,
|
|
|
|
|
| HValue* HGraphBuilder::AddLoadJSBuiltin(Builtins::JavaScript builtin) {
|
| - HGlobalObject* global_object = Add<HGlobalObject>();
|
| - HObjectAccess access = HObjectAccess::ForJSObjectOffset(
|
| + HValue* global_object = Add<HLoadNamedField>(
|
| + context(), static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForContextSlot(Context::GLOBAL_OBJECT_INDEX));
|
| + HObjectAccess access = HObjectAccess::ForObservableJSObjectOffset(
|
| GlobalObject::kBuiltinsOffset);
|
| - HValue* builtins = Add<HLoadNamedField>(global_object, access);
|
| - HObjectAccess function_access = HObjectAccess::ForJSObjectOffset(
|
| - JSBuiltinsObject::OffsetOfFunctionWithId(builtin));
|
| - return Add<HLoadNamedField>(builtins, function_access);
|
| + HValue* builtins = Add<HLoadNamedField>(
|
| + global_object, static_cast<HValue*>(NULL), access);
|
| + HObjectAccess function_access = HObjectAccess::ForObservableJSObjectOffset(
|
| + JSBuiltinsObject::OffsetOfFunctionWithId(builtin));
|
| + return Add<HLoadNamedField>(
|
| + builtins, static_cast<HValue*>(NULL), function_access);
|
| }
|
|
|
|
|
| HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info)
|
| : HGraphBuilder(info),
|
| function_state_(NULL),
|
| - initial_function_state_(this, info, NORMAL_RETURN),
|
| + initial_function_state_(this, info, NORMAL_RETURN, 0),
|
| ast_context_(NULL),
|
| break_scope_(NULL),
|
| inlined_count_(0),
|
| @@ -2902,8 +2992,8 @@ HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info)
|
| // constructor for the initial state relies on function_state_ == NULL
|
| // to know it's the initial state.
|
| function_state_= &initial_function_state_;
|
| - InitializeAstVisitor(info->isolate());
|
| - if (FLAG_emit_opt_code_positions) {
|
| + InitializeAstVisitor(info->zone());
|
| + if (FLAG_hydrogen_track_positions) {
|
| SetSourcePosition(info->shared_info()->start_position());
|
| }
|
| }
|
| @@ -2972,7 +3062,8 @@ HBasicBlock* HOptimizedGraphBuilder::BuildLoopEntry(
|
| }
|
|
|
|
|
| -void HBasicBlock::FinishExit(HControlInstruction* instruction, int position) {
|
| +void HBasicBlock::FinishExit(HControlInstruction* instruction,
|
| + HSourcePosition position) {
|
| Finish(instruction, position);
|
| ClearEnvironment();
|
| }
|
| @@ -2995,7 +3086,9 @@ HGraph::HGraph(CompilationInfo* info)
|
| type_change_checksum_(0),
|
| maximum_environment_size_(0),
|
| no_side_effects_scope_count_(0),
|
| - disallow_adding_new_values_(false) {
|
| + disallow_adding_new_values_(false),
|
| + next_inline_id_(0),
|
| + inlined_functions_(5, info->zone()) {
|
| if (info->IsStub()) {
|
| HydrogenCodeStub* stub = info->code_stub();
|
| CodeStubInterfaceDescriptor* descriptor =
|
| @@ -3003,6 +3096,7 @@ HGraph::HGraph(CompilationInfo* info)
|
| start_environment_ =
|
| new(zone_) HEnvironment(zone_, descriptor->environment_length());
|
| } else {
|
| + TraceInlinedFunction(info->shared_info(), HSourcePosition::Unknown());
|
| start_environment_ =
|
| new(zone_) HEnvironment(NULL, info->scope(), info->closure(), zone_);
|
| }
|
| @@ -3030,6 +3124,81 @@ void HGraph::FinalizeUniqueness() {
|
| }
|
|
|
|
|
| +int HGraph::TraceInlinedFunction(
|
| + Handle<SharedFunctionInfo> shared,
|
| + HSourcePosition position) {
|
| + if (!FLAG_hydrogen_track_positions) {
|
| + return 0;
|
| + }
|
| +
|
| + int id = 0;
|
| + for (; id < inlined_functions_.length(); id++) {
|
| + if (inlined_functions_[id].shared().is_identical_to(shared)) {
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (id == inlined_functions_.length()) {
|
| + inlined_functions_.Add(InlinedFunctionInfo(shared), zone());
|
| +
|
| + if (!shared->script()->IsUndefined()) {
|
| + Handle<Script> script(Script::cast(shared->script()));
|
| + if (!script->source()->IsUndefined()) {
|
| + CodeTracer::Scope tracing_scope(isolate()->GetCodeTracer());
|
| + PrintF(tracing_scope.file(),
|
| + "--- FUNCTION SOURCE (%s) id{%d,%d} ---\n",
|
| + shared->DebugName()->ToCString().get(),
|
| + info()->optimization_id(),
|
| + id);
|
| +
|
| + {
|
| + ConsStringIteratorOp op;
|
| + StringCharacterStream stream(String::cast(script->source()),
|
| + &op,
|
| + shared->start_position());
|
| + // fun->end_position() points to the last character in the stream. We
|
| + // need to compensate by adding one to calculate the length.
|
| + int source_len =
|
| + shared->end_position() - shared->start_position() + 1;
|
| + for (int i = 0; i < source_len; i++) {
|
| + if (stream.HasMore()) {
|
| + PrintF(tracing_scope.file(), "%c", stream.GetNext());
|
| + }
|
| + }
|
| + }
|
| +
|
| + PrintF(tracing_scope.file(), "\n--- END ---\n");
|
| + }
|
| + }
|
| + }
|
| +
|
| + int inline_id = next_inline_id_++;
|
| +
|
| + if (inline_id != 0) {
|
| + CodeTracer::Scope tracing_scope(isolate()->GetCodeTracer());
|
| + PrintF(tracing_scope.file(), "INLINE (%s) id{%d,%d} AS %d AT ",
|
| + shared->DebugName()->ToCString().get(),
|
| + info()->optimization_id(),
|
| + id,
|
| + inline_id);
|
| + position.PrintTo(tracing_scope.file());
|
| + PrintF(tracing_scope.file(), "\n");
|
| + }
|
| +
|
| + return inline_id;
|
| +}
|
| +
|
| +
|
| +int HGraph::SourcePositionToScriptPosition(HSourcePosition pos) {
|
| + if (!FLAG_hydrogen_track_positions || pos.IsUnknown()) {
|
| + return pos.raw();
|
| + }
|
| +
|
| + return inlined_functions_[pos.inlining_id()].start_position() +
|
| + pos.position();
|
| +}
|
| +
|
| +
|
| // Block ordering was implemented with two mutually recursive methods,
|
| // HGraph::Postorder and HGraph::PostorderLoopBlocks.
|
| // The recursion could lead to stack overflow so the algorithm has been
|
| @@ -3408,7 +3577,8 @@ void HGraph::CollectPhis() {
|
| // a (possibly inlined) function.
|
| FunctionState::FunctionState(HOptimizedGraphBuilder* owner,
|
| CompilationInfo* info,
|
| - InliningKind inlining_kind)
|
| + InliningKind inlining_kind,
|
| + int inlining_id)
|
| : owner_(owner),
|
| compilation_info_(info),
|
| call_context_(NULL),
|
| @@ -3418,6 +3588,8 @@ FunctionState::FunctionState(HOptimizedGraphBuilder* owner,
|
| entry_(NULL),
|
| arguments_object_(NULL),
|
| arguments_elements_(NULL),
|
| + inlining_id_(inlining_id),
|
| + outer_source_position_(HSourcePosition::Unknown()),
|
| outer_(owner->function_state()) {
|
| if (outer_ != NULL) {
|
| // State for an inline function.
|
| @@ -3441,12 +3613,27 @@ FunctionState::FunctionState(HOptimizedGraphBuilder* owner,
|
|
|
| // Push on the state stack.
|
| owner->set_function_state(this);
|
| +
|
| + if (FLAG_hydrogen_track_positions) {
|
| + outer_source_position_ = owner->source_position();
|
| + owner->EnterInlinedSource(
|
| + info->shared_info()->start_position(),
|
| + inlining_id);
|
| + owner->SetSourcePosition(info->shared_info()->start_position());
|
| + }
|
| }
|
|
|
|
|
| FunctionState::~FunctionState() {
|
| delete test_context_;
|
| owner_->set_function_state(outer_);
|
| +
|
| + if (FLAG_hydrogen_track_positions) {
|
| + owner_->set_source_position(outer_source_position_);
|
| + owner_->EnterInlinedSource(
|
| + outer_->compilation_info()->shared_info()->start_position(),
|
| + outer_->inlining_id());
|
| + }
|
| }
|
|
|
|
|
| @@ -3915,7 +4102,13 @@ void HGraph::RestoreActualValues() {
|
|
|
| for (HInstructionIterator it(block); !it.Done(); it.Advance()) {
|
| HInstruction* instruction = it.Current();
|
| - if (instruction->ActualValue() != instruction) {
|
| + if (instruction->ActualValue() == instruction) continue;
|
| + if (instruction->CheckFlag(HValue::kIsDead)) {
|
| + // The instruction was marked as deleted but left in the graph
|
| + // as a control flow dependency point for subsequent
|
| + // instructions.
|
| + instruction->DeleteAndReplaceWith(instruction->ActualValue());
|
| + } else {
|
| ASSERT(instruction->IsInformativeDefinition());
|
| if (instruction->IsPurelyInformativeDefinition()) {
|
| instruction->DeleteAndReplaceWith(instruction->RedefinedOperand());
|
| @@ -4233,7 +4426,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
| CHECK_ALIVE(VisitForValue(stmt->tag()));
|
| Add<HSimulate>(stmt->EntryId());
|
| HValue* tag_value = Top();
|
| - Handle<Type> tag_type = stmt->tag()->bounds().lower;
|
| + Type* tag_type = stmt->tag()->bounds().lower;
|
|
|
| // 1. Build all the tests, with dangling true branches
|
| BailoutId default_id = BailoutId::None();
|
| @@ -4249,12 +4442,14 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
| CHECK_ALIVE(VisitForValue(clause->label()));
|
| HValue* label_value = Pop();
|
|
|
| - Handle<Type> label_type = clause->label()->bounds().lower;
|
| - Handle<Type> combined_type = clause->compare_type();
|
| + Type* label_type = clause->label()->bounds().lower;
|
| + Type* combined_type = clause->compare_type();
|
| HControlInstruction* compare = BuildCompareInstruction(
|
| Token::EQ_STRICT, tag_value, label_value, tag_type, label_type,
|
| - combined_type, stmt->tag()->position(), clause->label()->position(),
|
| - clause->id());
|
| + combined_type,
|
| + ScriptPositionToSourcePosition(stmt->tag()->position()),
|
| + ScriptPositionToSourcePosition(clause->label()->position()),
|
| + PUSH_BEFORE_SIMULATE, clause->id());
|
|
|
| HBasicBlock* next_test_block = graph()->CreateBasicBlock();
|
| HBasicBlock* body_block = graph()->CreateBasicBlock();
|
| @@ -4674,14 +4869,14 @@ void HOptimizedGraphBuilder::VisitConditional(Conditional* expr) {
|
|
|
| HOptimizedGraphBuilder::GlobalPropertyAccess
|
| HOptimizedGraphBuilder::LookupGlobalProperty(
|
| - Variable* var, LookupResult* lookup, bool is_store) {
|
| + Variable* var, LookupResult* lookup, PropertyAccessType access_type) {
|
| if (var->is_this() || !current_info()->has_global_object()) {
|
| return kUseGeneric;
|
| }
|
| Handle<GlobalObject> global(current_info()->global_object());
|
| global->Lookup(*var->name(), lookup);
|
| if (!lookup->IsNormal() ||
|
| - (is_store && lookup->IsReadOnly()) ||
|
| + (access_type == STORE && lookup->IsReadOnly()) ||
|
| lookup->holder() != *global) {
|
| return kUseGeneric;
|
| }
|
| @@ -4695,7 +4890,9 @@ HValue* HOptimizedGraphBuilder::BuildContextChainWalk(Variable* var) {
|
| HValue* context = environment()->context();
|
| int length = current_info()->scope()->ContextChainLength(var->scope());
|
| while (length-- > 0) {
|
| - context = Add<HOuterContext>(context);
|
| + context = Add<HLoadNamedField>(
|
| + context, static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForContextSlot(Context::PREVIOUS_INDEX));
|
| }
|
| return context;
|
| }
|
| @@ -4726,8 +4923,7 @@ void HOptimizedGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
|
| }
|
|
|
| LookupResult lookup(isolate());
|
| - GlobalPropertyAccess type =
|
| - LookupGlobalProperty(variable, &lookup, false);
|
| + GlobalPropertyAccess type = LookupGlobalProperty(variable, &lookup, LOAD);
|
|
|
| if (type == kUseCell &&
|
| current_info()->global_object()->IsAccessCheckNeeded()) {
|
| @@ -4752,7 +4948,9 @@ void HOptimizedGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
|
| return ast_context()->ReturnInstruction(instr, expr->id());
|
| }
|
| } else {
|
| - HGlobalObject* global_object = Add<HGlobalObject>();
|
| + HValue* global_object = Add<HLoadNamedField>(
|
| + context(), static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForContextSlot(Context::GLOBAL_OBJECT_INDEX));
|
| HLoadGlobalGeneric* instr =
|
| New<HLoadGlobalGeneric>(global_object,
|
| variable->name(),
|
| @@ -4807,81 +5005,13 @@ void HOptimizedGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
|
| }
|
|
|
|
|
| -static bool CanInlinePropertyAccess(Map* type) {
|
| - return type->IsJSObjectMap() &&
|
| - !type->is_dictionary_map() &&
|
| - !type->has_named_interceptor();
|
| -}
|
| -
|
| -
|
| -static void LookupInPrototypes(Handle<Map> map,
|
| - Handle<String> name,
|
| - LookupResult* lookup) {
|
| - while (map->prototype()->IsJSObject()) {
|
| - Handle<JSObject> holder(JSObject::cast(map->prototype()));
|
| - map = Handle<Map>(holder->map());
|
| - if (!CanInlinePropertyAccess(*map)) break;
|
| - map->LookupDescriptor(*holder, *name, lookup);
|
| - if (lookup->IsFound()) return;
|
| - }
|
| - lookup->NotFound();
|
| -}
|
| -
|
| -
|
| -// Tries to find a JavaScript accessor of the given name in the prototype chain
|
| -// starting at the given map. Return true iff there is one, including the
|
| -// corresponding AccessorPair plus its holder (which could be null when the
|
| -// accessor is found directly in the given map).
|
| -static bool LookupAccessorPair(Handle<Map> map,
|
| - Handle<String> name,
|
| - Handle<AccessorPair>* accessors,
|
| - Handle<JSObject>* holder) {
|
| - Isolate* isolate = map->GetIsolate();
|
| - LookupResult lookup(isolate);
|
| -
|
| - // Check for a JavaScript accessor directly in the map.
|
| - map->LookupDescriptor(NULL, *name, &lookup);
|
| - if (lookup.IsPropertyCallbacks()) {
|
| - Handle<Object> callback(lookup.GetValueFromMap(*map), isolate);
|
| - if (!callback->IsAccessorPair()) return false;
|
| - *accessors = Handle<AccessorPair>::cast(callback);
|
| - *holder = Handle<JSObject>();
|
| - return true;
|
| - }
|
| -
|
| - // Everything else, e.g. a field, can't be an accessor call.
|
| - if (lookup.IsFound()) return false;
|
| -
|
| - // Check for a JavaScript accessor somewhere in the proto chain.
|
| - LookupInPrototypes(map, name, &lookup);
|
| - if (lookup.IsPropertyCallbacks()) {
|
| - Handle<Object> callback(lookup.GetValue(), isolate);
|
| - if (!callback->IsAccessorPair()) return false;
|
| - *accessors = Handle<AccessorPair>::cast(callback);
|
| - *holder = Handle<JSObject>(lookup.holder());
|
| - return true;
|
| - }
|
| -
|
| - // We haven't found a JavaScript accessor anywhere.
|
| - return false;
|
| -}
|
| -
|
| -
|
| -static bool LookupSetter(Handle<Map> map,
|
| - Handle<String> name,
|
| - Handle<JSFunction>* setter,
|
| - Handle<JSObject>* holder) {
|
| - Handle<AccessorPair> accessors;
|
| - if (LookupAccessorPair(map, name, &accessors, holder) &&
|
| - accessors->setter()->IsJSFunction()) {
|
| - Handle<JSFunction> func(JSFunction::cast(accessors->setter()));
|
| - CallOptimization call_optimization(func);
|
| - // TODO(dcarney): temporary hack unless crankshaft can handle api calls.
|
| - if (call_optimization.is_simple_api_call()) return false;
|
| - *setter = func;
|
| - return true;
|
| - }
|
| - return false;
|
| +static bool CanInlinePropertyAccess(Type* type) {
|
| + if (type->Is(Type::NumberOrString())) return true;
|
| + if (!type->IsClass()) return false;
|
| + Handle<Map> map = type->AsClass();
|
| + return map->IsJSObjectMap() &&
|
| + !map->is_dictionary_map() &&
|
| + !map->has_named_interceptor();
|
| }
|
|
|
|
|
| @@ -5028,17 +5158,20 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
|
| HInstruction* store;
|
| if (map.is_null()) {
|
| // If we don't know the monomorphic type, do a generic store.
|
| - CHECK_ALIVE(store = BuildStoreNamedGeneric(literal, name, value));
|
| + CHECK_ALIVE(store = BuildNamedGeneric(
|
| + STORE, literal, name, value));
|
| } else {
|
| -#if DEBUG
|
| - Handle<JSFunction> setter;
|
| - Handle<JSObject> holder;
|
| - ASSERT(!LookupSetter(map, name, &setter, &holder));
|
| -#endif
|
| - CHECK_ALIVE(store = BuildStoreNamedMonomorphic(literal,
|
| - name,
|
| - value,
|
| - map));
|
| + PropertyAccessInfo info(this, STORE, ToType(map), name);
|
| + if (info.CanAccessMonomorphic()) {
|
| + HValue* checked_literal = BuildCheckMap(literal, map);
|
| + ASSERT(!info.lookup()->IsPropertyCallbacks());
|
| + store = BuildMonomorphicAccess(
|
| + &info, literal, checked_literal, value,
|
| + BailoutId::None(), BailoutId::None());
|
| + } else {
|
| + CHECK_ALIVE(store = BuildNamedGeneric(
|
| + STORE, literal, name, value));
|
| + }
|
| }
|
| AddInstruction(store);
|
| if (store->HasObservableSideEffects()) {
|
| @@ -5213,55 +5346,48 @@ HCheckMaps* HOptimizedGraphBuilder::AddCheckMap(HValue* object,
|
| }
|
|
|
|
|
| -HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
|
| - HValue* checked_object,
|
| - Handle<String> name,
|
| - HValue* value,
|
| - Handle<Map> map,
|
| - LookupResult* lookup) {
|
| - ASSERT(lookup->IsFound());
|
| - // If the property does not exist yet, we have to check that it wasn't made
|
| - // readonly or turned into a setter by some meanwhile modifications on the
|
| - // prototype chain.
|
| - if (!lookup->IsProperty() && map->prototype()->IsJSReceiver()) {
|
| - Object* proto = map->prototype();
|
| - // First check that the prototype chain isn't affected already.
|
| - LookupResult proto_result(isolate());
|
| - proto->Lookup(*name, &proto_result);
|
| - if (proto_result.IsProperty()) {
|
| - // If the inherited property could induce readonly-ness, bail out.
|
| - if (proto_result.IsReadOnly() || !proto_result.IsCacheable()) {
|
| - Bailout(kImproperObjectOnPrototypeChainForStore);
|
| - return NULL;
|
| - }
|
| - // We only need to check up to the preexisting property.
|
| - proto = proto_result.holder();
|
| - } else {
|
| - // Otherwise, find the top prototype.
|
| - while (proto->GetPrototype(isolate())->IsJSObject()) {
|
| - proto = proto->GetPrototype(isolate());
|
| - }
|
| - ASSERT(proto->GetPrototype(isolate())->IsNull());
|
| - }
|
| - ASSERT(proto->IsJSObject());
|
| - BuildCheckPrototypeMaps(
|
| - Handle<JSObject>(JSObject::cast(map->prototype())),
|
| - Handle<JSObject>(JSObject::cast(proto)));
|
| +HInstruction* HOptimizedGraphBuilder::BuildLoadNamedField(
|
| + PropertyAccessInfo* info,
|
| + HValue* checked_object) {
|
| + HObjectAccess access = info->access();
|
| + if (access.representation().IsDouble()) {
|
| + // Load the heap number.
|
| + checked_object = Add<HLoadNamedField>(
|
| + checked_object, static_cast<HValue*>(NULL),
|
| + access.WithRepresentation(Representation::Tagged()));
|
| + checked_object->set_type(HType::HeapNumber());
|
| + // Load the double value from it.
|
| + access = HObjectAccess::ForHeapNumberValue();
|
| }
|
| + return New<HLoadNamedField>(
|
| + checked_object, static_cast<HValue*>(NULL), access);
|
| +}
|
| +
|
|
|
| - HObjectAccess field_access = HObjectAccess::ForField(map, lookup, name);
|
| - bool transition_to_field = lookup->IsTransitionToField(*map);
|
| +HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
|
| + PropertyAccessInfo* info,
|
| + HValue* checked_object,
|
| + HValue* value) {
|
| + bool transition_to_field = info->lookup()->IsTransition();
|
| + // TODO(verwaest): Move this logic into PropertyAccessInfo.
|
| + HObjectAccess field_access = HObjectAccess::ForField(
|
| + info->map(), info->lookup(), info->name());
|
|
|
| HStoreNamedField *instr;
|
| - if (FLAG_track_double_fields && field_access.representation().IsDouble()) {
|
| + if (field_access.representation().IsDouble()) {
|
| HObjectAccess heap_number_access =
|
| field_access.WithRepresentation(Representation::Tagged());
|
| if (transition_to_field) {
|
| // The store requires a mutable HeapNumber to be allocated.
|
| NoObservableSideEffectsScope no_side_effects(this);
|
| HInstruction* heap_number_size = Add<HConstant>(HeapNumber::kSize);
|
| +
|
| + PretenureFlag pretenure_flag = !FLAG_allocation_site_pretenuring ?
|
| + isolate()->heap()->GetPretenureMode() : NOT_TENURED;
|
| +
|
| HInstruction* heap_number = Add<HAllocate>(heap_number_size,
|
| - HType::HeapNumber(), isolate()->heap()->GetPretenureMode(),
|
| + HType::HeapNumber(),
|
| + pretenure_flag,
|
| HEAP_NUMBER_TYPE);
|
| AddStoreMapConstant(heap_number, isolate()->factory()->heap_number_map());
|
| Add<HStoreNamedField>(heap_number, HObjectAccess::ForHeapNumberValue(),
|
| @@ -5271,12 +5397,12 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
|
| heap_number);
|
| } else {
|
| // Already holds a HeapNumber; load the box and write its value field.
|
| - HInstruction* heap_number = Add<HLoadNamedField>(checked_object,
|
| - heap_number_access);
|
| + HInstruction* heap_number = Add<HLoadNamedField>(
|
| + checked_object, static_cast<HValue*>(NULL), heap_number_access);
|
| heap_number->set_type(HType::HeapNumber());
|
| instr = New<HStoreNamedField>(heap_number,
|
| HObjectAccess::ForHeapNumberValue(),
|
| - value);
|
| + value, STORE_TO_INITIALIZED_ENTRY);
|
| }
|
| } else {
|
| // This is a normal store.
|
| @@ -5286,77 +5412,36 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
|
| }
|
|
|
| if (transition_to_field) {
|
| - Handle<Map> transition(lookup->GetTransitionMapFromMap(*map));
|
| - HConstant* transition_constant = Add<HConstant>(transition);
|
| + HConstant* transition_constant = Add<HConstant>(info->transition());
|
| instr->SetTransition(transition_constant, top_info());
|
| - // TODO(fschneider): Record the new map type of the object in the IR to
|
| - // enable elimination of redundant checks after the transition store.
|
| - instr->SetGVNFlag(kChangesMaps);
|
| + instr->SetChangesFlag(kMaps);
|
| }
|
| return instr;
|
| }
|
|
|
|
|
| -HInstruction* HOptimizedGraphBuilder::BuildStoreNamedGeneric(
|
| - HValue* object,
|
| - Handle<String> name,
|
| - HValue* value) {
|
| - return New<HStoreNamedGeneric>(
|
| - object,
|
| - name,
|
| - value,
|
| - function_strict_mode_flag());
|
| -}
|
| -
|
| -
|
| -// Sets the lookup result and returns true if the load/store can be inlined.
|
| -static bool ComputeStoreField(Handle<Map> type,
|
| - Handle<String> name,
|
| - LookupResult* lookup,
|
| - bool lookup_transition = true) {
|
| - ASSERT(!type->is_observed());
|
| - if (!CanInlinePropertyAccess(*type)) {
|
| - lookup->NotFound();
|
| - return false;
|
| - }
|
| - // If we directly find a field, the access can be inlined.
|
| - type->LookupDescriptor(NULL, *name, lookup);
|
| - if (lookup->IsField()) return true;
|
| -
|
| - if (!lookup_transition) return false;
|
| -
|
| - type->LookupTransition(NULL, *name, lookup);
|
| - return lookup->IsTransitionToField(*type) &&
|
| - (type->unused_property_fields() > 0);
|
| -}
|
| +bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatible(
|
| + PropertyAccessInfo* info) {
|
| + if (!CanInlinePropertyAccess(type_)) return false;
|
|
|
| + // Currently only handle Type::Number as a polymorphic case.
|
| + // TODO(verwaest): Support monomorphic handling of numbers with a HCheckNumber
|
| + // instruction.
|
| + if (type_->Is(Type::Number())) return false;
|
|
|
| -HInstruction* HOptimizedGraphBuilder::BuildStoreNamedMonomorphic(
|
| - HValue* object,
|
| - Handle<String> name,
|
| - HValue* value,
|
| - Handle<Map> map) {
|
| - // Handle a store to a known field.
|
| - LookupResult lookup(isolate());
|
| - if (ComputeStoreField(map, name, &lookup)) {
|
| - HCheckMaps* checked_object = AddCheckMap(object, map);
|
| - return BuildStoreNamedField(checked_object, name, value, map, &lookup);
|
| + // Values are only compatible for monomorphic load if they all behave the same
|
| + // regarding value wrappers.
|
| + if (type_->Is(Type::NumberOrString())) {
|
| + if (!info->type_->Is(Type::NumberOrString())) return false;
|
| + } else {
|
| + if (info->type_->Is(Type::NumberOrString())) return false;
|
| }
|
|
|
| - // No luck, do a generic store.
|
| - return BuildStoreNamedGeneric(object, name, value);
|
| -}
|
| -
|
| -
|
| -bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatibleForLoad(
|
| - PropertyAccessInfo* info) {
|
| - if (!CanInlinePropertyAccess(*map_)) return false;
|
| -
|
| if (!LookupDescriptor()) return false;
|
|
|
| if (!lookup_.IsFound()) {
|
| return (!info->lookup_.IsFound() || info->has_holder()) &&
|
| - map_->prototype() == info->map_->prototype();
|
| + map()->prototype() == info->map()->prototype();
|
| }
|
|
|
| // Mismatch if the other access info found the property in the prototype
|
| @@ -5364,7 +5449,8 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatibleForLoad(
|
| if (info->has_holder()) return false;
|
|
|
| if (lookup_.IsPropertyCallbacks()) {
|
| - return accessor_.is_identical_to(info->accessor_);
|
| + return accessor_.is_identical_to(info->accessor_) &&
|
| + api_holder_.is_identical_to(info->api_holder_);
|
| }
|
|
|
| if (lookup_.IsConstant()) {
|
| @@ -5375,7 +5461,11 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatibleForLoad(
|
| if (!info->lookup_.IsField()) return false;
|
|
|
| Representation r = access_.representation();
|
| - if (!info->access_.representation().IsCompatibleForLoad(r)) return false;
|
| + if (IsLoad()) {
|
| + if (!info->access_.representation().IsCompatibleForLoad(r)) return false;
|
| + } else {
|
| + if (!info->access_.representation().IsCompatibleForStore(r)) return false;
|
| + }
|
| if (info->access_.offset() != access_.offset()) return false;
|
| if (info->access_.IsInobject() != access_.IsInobject()) return false;
|
| info->GeneralizeRepresentation(r);
|
| @@ -5384,23 +5474,42 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatibleForLoad(
|
|
|
|
|
| bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupDescriptor() {
|
| - map_->LookupDescriptor(NULL, *name_, &lookup_);
|
| - return LoadResult(map_);
|
| + if (!type_->IsClass()) return true;
|
| + map()->LookupDescriptor(NULL, *name_, &lookup_);
|
| + return LoadResult(map());
|
| }
|
|
|
|
|
| bool HOptimizedGraphBuilder::PropertyAccessInfo::LoadResult(Handle<Map> map) {
|
| + if (!IsLoad() && lookup_.IsProperty() &&
|
| + (lookup_.IsReadOnly() || !lookup_.IsCacheable())) {
|
| + return false;
|
| + }
|
| +
|
| if (lookup_.IsField()) {
|
| access_ = HObjectAccess::ForField(map, &lookup_, name_);
|
| } else if (lookup_.IsPropertyCallbacks()) {
|
| Handle<Object> callback(lookup_.GetValueFromMap(*map), isolate());
|
| if (!callback->IsAccessorPair()) return false;
|
| - Object* getter = Handle<AccessorPair>::cast(callback)->getter();
|
| - if (!getter->IsJSFunction()) return false;
|
| - Handle<JSFunction> accessor = handle(JSFunction::cast(getter));
|
| - CallOptimization call_optimization(accessor);
|
| - // TODO(dcarney): temporary hack unless crankshaft can handle api calls.
|
| - if (call_optimization.is_simple_api_call()) return false;
|
| + Object* raw_accessor = IsLoad()
|
| + ? Handle<AccessorPair>::cast(callback)->getter()
|
| + : Handle<AccessorPair>::cast(callback)->setter();
|
| + if (!raw_accessor->IsJSFunction()) return false;
|
| + Handle<JSFunction> accessor = handle(JSFunction::cast(raw_accessor));
|
| + if (accessor->shared()->IsApiFunction()) {
|
| + CallOptimization call_optimization(accessor);
|
| + if (!call_optimization.is_simple_api_call()) return false;
|
| + CallOptimization::HolderLookup holder_lookup;
|
| + api_holder_ = call_optimization.LookupHolderOfExpectedType(
|
| + map, &holder_lookup);
|
| + switch (holder_lookup) {
|
| + case CallOptimization::kHolderNotFound:
|
| + return false;
|
| + case CallOptimization::kHolderIsReceiver:
|
| + case CallOptimization::kHolderFound:
|
| + break;
|
| + }
|
| + }
|
| accessor_ = accessor;
|
| } else if (lookup_.IsConstant()) {
|
| constant_ = handle(lookup_.GetConstantFromMap(*map), isolate());
|
| @@ -5411,14 +5520,15 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LoadResult(Handle<Map> map) {
|
|
|
|
|
| bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupInPrototypes() {
|
| - Handle<Map> map = map_;
|
| + Handle<Map> map = this->map();
|
| +
|
| while (map->prototype()->IsJSObject()) {
|
| holder_ = handle(JSObject::cast(map->prototype()));
|
| if (holder_->map()->is_deprecated()) {
|
| JSObject::TryMigrateInstance(holder_);
|
| }
|
| map = Handle<Map>(holder_->map());
|
| - if (!CanInlinePropertyAccess(*map)) {
|
| + if (!CanInlinePropertyAccess(ToType(map))) {
|
| lookup_.NotFound();
|
| return false;
|
| }
|
| @@ -5430,68 +5540,85 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupInPrototypes() {
|
| }
|
|
|
|
|
| -bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadMonomorphic() {
|
| - if (!CanInlinePropertyAccess(*map_)) return IsStringLength();
|
| - if (IsJSObjectFieldAccessor()) return true;
|
| +bool HOptimizedGraphBuilder::PropertyAccessInfo::CanAccessMonomorphic() {
|
| + if (!CanInlinePropertyAccess(type_)) return false;
|
| + if (IsJSObjectFieldAccessor()) return IsLoad();
|
| if (!LookupDescriptor()) return false;
|
| - if (lookup_.IsFound()) return true;
|
| - return LookupInPrototypes();
|
| + if (lookup_.IsFound()) {
|
| + if (IsLoad()) return true;
|
| + return !lookup_.IsReadOnly() && lookup_.IsCacheable();
|
| + }
|
| + if (!LookupInPrototypes()) return false;
|
| + if (IsLoad()) return true;
|
| +
|
| + if (lookup_.IsPropertyCallbacks()) return true;
|
| + Handle<Map> map = this->map();
|
| + map->LookupTransition(NULL, *name_, &lookup_);
|
| + if (lookup_.IsTransitionToField() && map->unused_property_fields() > 0) {
|
| + return true;
|
| + }
|
| + return false;
|
| }
|
|
|
|
|
| -bool HOptimizedGraphBuilder::PropertyAccessInfo::CanLoadAsMonomorphic(
|
| +bool HOptimizedGraphBuilder::PropertyAccessInfo::CanAccessAsMonomorphic(
|
| SmallMapList* types) {
|
| - ASSERT(map_.is_identical_to(types->first()));
|
| - if (!CanLoadMonomorphic()) return false;
|
| + ASSERT(type_->Is(ToType(types->first())));
|
| + if (!CanAccessMonomorphic()) return false;
|
| + STATIC_ASSERT(kMaxLoadPolymorphism == kMaxStorePolymorphism);
|
| if (types->length() > kMaxLoadPolymorphism) return false;
|
|
|
| - if (IsStringLength()) {
|
| + HObjectAccess access = HObjectAccess::ForMap(); // bogus default
|
| + if (GetJSObjectFieldAccess(&access)) {
|
| for (int i = 1; i < types->length(); ++i) {
|
| - if (types->at(i)->instance_type() >= FIRST_NONSTRING_TYPE) return false;
|
| + PropertyAccessInfo test_info(
|
| + builder_, access_type_, ToType(types->at(i)), name_);
|
| + HObjectAccess test_access = HObjectAccess::ForMap(); // bogus default
|
| + if (!test_info.GetJSObjectFieldAccess(&test_access)) return false;
|
| + if (!access.Equals(test_access)) return false;
|
| }
|
| return true;
|
| }
|
|
|
| - if (IsArrayLength()) {
|
| - bool is_fast = IsFastElementsKind(map_->elements_kind());
|
| - for (int i = 1; i < types->length(); ++i) {
|
| - Handle<Map> test_map = types->at(i);
|
| - if (test_map->instance_type() != JS_ARRAY_TYPE) return false;
|
| - if (IsFastElementsKind(test_map->elements_kind()) != is_fast) {
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| - }
|
| + // Currently only handle Type::Number as a polymorphic case.
|
| + // TODO(verwaest): Support monomorphic handling of numbers with a HCheckNumber
|
| + // instruction.
|
| + if (type_->Is(Type::Number())) return false;
|
|
|
| - if (IsJSObjectFieldAccessor()) {
|
| - InstanceType instance_type = map_->instance_type();
|
| - for (int i = 1; i < types->length(); ++i) {
|
| - if (types->at(i)->instance_type() != instance_type) return false;
|
| - }
|
| - return true;
|
| - }
|
| + // Multiple maps cannot transition to the same target map.
|
| + ASSERT(!IsLoad() || !lookup_.IsTransition());
|
| + if (lookup_.IsTransition() && types->length() > 1) return false;
|
|
|
| for (int i = 1; i < types->length(); ++i) {
|
| - PropertyAccessInfo test_info(isolate(), types->at(i), name_);
|
| - if (!test_info.IsCompatibleForLoad(this)) return false;
|
| + PropertyAccessInfo test_info(
|
| + builder_, access_type_, ToType(types->at(i)), name_);
|
| + if (!test_info.IsCompatible(this)) return false;
|
| }
|
|
|
| return true;
|
| }
|
|
|
|
|
| -HInstruction* HOptimizedGraphBuilder::BuildLoadMonomorphic(
|
| +static bool NeedsWrappingFor(Type* type, Handle<JSFunction> target) {
|
| + return type->Is(Type::NumberOrString()) &&
|
| + target->shared()->is_classic_mode() &&
|
| + !target->shared()->native();
|
| +}
|
| +
|
| +
|
| +HInstruction* HOptimizedGraphBuilder::BuildMonomorphicAccess(
|
| PropertyAccessInfo* info,
|
| HValue* object,
|
| - HInstruction* checked_object,
|
| + HValue* checked_object,
|
| + HValue* value,
|
| BailoutId ast_id,
|
| BailoutId return_id,
|
| bool can_inline_accessor) {
|
|
|
| HObjectAccess access = HObjectAccess::ForMap(); // bogus default
|
| if (info->GetJSObjectFieldAccess(&access)) {
|
| - return New<HLoadNamedField>(checked_object, access);
|
| + ASSERT(info->IsLoad());
|
| + return New<HLoadNamedField>(object, checked_object, access);
|
| }
|
|
|
| HValue* checked_holder = checked_object;
|
| @@ -5500,81 +5627,175 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadMonomorphic(
|
| checked_holder = BuildCheckPrototypeMaps(prototype, info->holder());
|
| }
|
|
|
| - if (!info->lookup()->IsFound()) return graph()->GetConstantUndefined();
|
| + if (!info->lookup()->IsFound()) {
|
| + ASSERT(info->IsLoad());
|
| + return graph()->GetConstantUndefined();
|
| + }
|
|
|
| if (info->lookup()->IsField()) {
|
| - return BuildLoadNamedField(checked_holder, info->access());
|
| + if (info->IsLoad()) {
|
| + return BuildLoadNamedField(info, checked_holder);
|
| + } else {
|
| + return BuildStoreNamedField(info, checked_object, value);
|
| + }
|
| + }
|
| +
|
| + if (info->lookup()->IsTransition()) {
|
| + ASSERT(!info->IsLoad());
|
| + return BuildStoreNamedField(info, checked_object, value);
|
| }
|
|
|
| if (info->lookup()->IsPropertyCallbacks()) {
|
| Push(checked_object);
|
| - if (FLAG_inline_accessors &&
|
| - can_inline_accessor &&
|
| - TryInlineGetter(info->accessor(), ast_id, return_id)) {
|
| - return NULL;
|
| + int argument_count = 1;
|
| + if (!info->IsLoad()) {
|
| + argument_count = 2;
|
| + Push(value);
|
| + }
|
| +
|
| + if (NeedsWrappingFor(info->type(), info->accessor())) {
|
| + HValue* function = Add<HConstant>(info->accessor());
|
| + PushArgumentsFromEnvironment(argument_count);
|
| + return New<HCallFunction>(function, argument_count, WRAP_AND_CALL);
|
| + } else if (FLAG_inline_accessors && can_inline_accessor) {
|
| + bool success = info->IsLoad()
|
| + ? TryInlineGetter(info->accessor(), info->map(), ast_id, return_id)
|
| + : TryInlineSetter(
|
| + info->accessor(), info->map(), ast_id, return_id, value);
|
| + if (success) return NULL;
|
| }
|
| - Add<HPushArgument>(Pop());
|
| - return BuildCallConstantFunction(info->accessor(), 1);
|
| +
|
| + PushArgumentsFromEnvironment(argument_count);
|
| + return BuildCallConstantFunction(info->accessor(), argument_count);
|
| }
|
|
|
| ASSERT(info->lookup()->IsConstant());
|
| - return New<HConstant>(info->constant());
|
| + if (info->IsLoad()) {
|
| + return New<HConstant>(info->constant());
|
| + } else {
|
| + return New<HCheckValue>(value, Handle<JSFunction>::cast(info->constant()));
|
| + }
|
| }
|
|
|
|
|
| -void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(
|
| +void HOptimizedGraphBuilder::HandlePolymorphicNamedFieldAccess(
|
| + PropertyAccessType access_type,
|
| BailoutId ast_id,
|
| BailoutId return_id,
|
| HValue* object,
|
| + HValue* value,
|
| SmallMapList* types,
|
| Handle<String> name) {
|
| // Something did not match; must use a polymorphic load.
|
| int count = 0;
|
| HBasicBlock* join = NULL;
|
| + HBasicBlock* number_block = NULL;
|
| + bool handled_string = false;
|
| +
|
| + bool handle_smi = false;
|
| + STATIC_ASSERT(kMaxLoadPolymorphism == kMaxStorePolymorphism);
|
| for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) {
|
| - PropertyAccessInfo info(isolate(), types->at(i), name);
|
| - if (info.CanLoadMonomorphic()) {
|
| - if (count == 0) {
|
| - BuildCheckHeapObject(object);
|
| - join = graph()->CreateBasicBlock();
|
| + PropertyAccessInfo info(this, access_type, ToType(types->at(i)), name);
|
| + if (info.type()->Is(Type::String())) {
|
| + if (handled_string) continue;
|
| + handled_string = true;
|
| + }
|
| + if (info.CanAccessMonomorphic()) {
|
| + count++;
|
| + if (info.type()->Is(Type::Number())) {
|
| + handle_smi = true;
|
| + break;
|
| }
|
| - ++count;
|
| - HBasicBlock* if_true = graph()->CreateBasicBlock();
|
| - HBasicBlock* if_false = graph()->CreateBasicBlock();
|
| - HCompareMap* compare = New<HCompareMap>(
|
| - object, info.map(), if_true, if_false);
|
| - FinishCurrentBlock(compare);
|
| -
|
| - set_current_block(if_true);
|
| -
|
| - HInstruction* load = BuildLoadMonomorphic(
|
| - &info, object, compare, ast_id, return_id, FLAG_polymorphic_inlining);
|
| - if (load == NULL) {
|
| - if (HasStackOverflow()) return;
|
| + }
|
| + }
|
| +
|
| + count = 0;
|
| + HControlInstruction* smi_check = NULL;
|
| + handled_string = false;
|
| +
|
| + for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) {
|
| + PropertyAccessInfo info(this, access_type, ToType(types->at(i)), name);
|
| + if (info.type()->Is(Type::String())) {
|
| + if (handled_string) continue;
|
| + handled_string = true;
|
| + }
|
| + if (!info.CanAccessMonomorphic()) continue;
|
| +
|
| + if (count == 0) {
|
| + join = graph()->CreateBasicBlock();
|
| + if (handle_smi) {
|
| + HBasicBlock* empty_smi_block = graph()->CreateBasicBlock();
|
| + HBasicBlock* not_smi_block = graph()->CreateBasicBlock();
|
| + number_block = graph()->CreateBasicBlock();
|
| + smi_check = New<HIsSmiAndBranch>(
|
| + object, empty_smi_block, not_smi_block);
|
| + FinishCurrentBlock(smi_check);
|
| + GotoNoSimulate(empty_smi_block, number_block);
|
| + set_current_block(not_smi_block);
|
| } else {
|
| - if (!load->IsLinked()) {
|
| - AddInstruction(load);
|
| - }
|
| - if (!ast_context()->IsEffect()) Push(load);
|
| + BuildCheckHeapObject(object);
|
| }
|
| + }
|
| + ++count;
|
| + HBasicBlock* if_true = graph()->CreateBasicBlock();
|
| + HBasicBlock* if_false = graph()->CreateBasicBlock();
|
| + HUnaryControlInstruction* compare;
|
| +
|
| + HValue* dependency;
|
| + if (info.type()->Is(Type::Number())) {
|
| + Handle<Map> heap_number_map = isolate()->factory()->heap_number_map();
|
| + compare = New<HCompareMap>(object, heap_number_map, if_true, if_false);
|
| + dependency = smi_check;
|
| + } else if (info.type()->Is(Type::String())) {
|
| + compare = New<HIsStringAndBranch>(object, if_true, if_false);
|
| + dependency = compare;
|
| + } else {
|
| + compare = New<HCompareMap>(object, info.map(), if_true, if_false);
|
| + dependency = compare;
|
| + }
|
| + FinishCurrentBlock(compare);
|
| +
|
| + if (info.type()->Is(Type::Number())) {
|
| + GotoNoSimulate(if_true, number_block);
|
| + if_true = number_block;
|
| + }
|
| +
|
| + set_current_block(if_true);
|
| +
|
| + HInstruction* access = BuildMonomorphicAccess(
|
| + &info, object, dependency, value, ast_id,
|
| + return_id, FLAG_polymorphic_inlining);
|
|
|
| - if (current_block() != NULL) Goto(join);
|
| - set_current_block(if_false);
|
| + HValue* result = NULL;
|
| + switch (access_type) {
|
| + case LOAD:
|
| + result = access;
|
| + break;
|
| + case STORE:
|
| + result = value;
|
| + break;
|
| + }
|
| +
|
| + if (access == NULL) {
|
| + if (HasStackOverflow()) return;
|
| + } else {
|
| + if (!access->IsLinked()) AddInstruction(access);
|
| + if (!ast_context()->IsEffect()) Push(result);
|
| }
|
| +
|
| + if (current_block() != NULL) Goto(join);
|
| + set_current_block(if_false);
|
| }
|
|
|
| // Finish up. Unconditionally deoptimize if we've handled all the maps we
|
| // know about and do not want to handle ones we've never seen. Otherwise
|
| // use a generic IC.
|
| if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
|
| - // Because the deopt may be the only path in the polymorphic load, make sure
|
| - // that the environment stack matches the depth on deopt that it otherwise
|
| - // would have had after a successful load.
|
| - if (!ast_context()->IsEffect()) Push(graph()->GetConstant0());
|
| - FinishExitWithHardDeoptimization("Unknown map in polymorphic load", join);
|
| + FinishExitWithHardDeoptimization("Uknown map in polymorphic access");
|
| } else {
|
| - HInstruction* load = Add<HLoadNamedGeneric>(object, name);
|
| - if (!ast_context()->IsEffect()) Push(load);
|
| + HInstruction* instr = BuildNamedGeneric(access_type, object, name, value);
|
| + AddInstruction(instr);
|
| + if (!ast_context()->IsEffect()) Push(access_type == LOAD ? instr : value);
|
|
|
| if (join != NULL) {
|
| Goto(join);
|
| @@ -5586,156 +5807,20 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(
|
| }
|
|
|
| ASSERT(join != NULL);
|
| - join->SetJoinId(ast_id);
|
| - set_current_block(join);
|
| - if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
|
| -}
|
| -
|
| -
|
| -bool HOptimizedGraphBuilder::TryStorePolymorphicAsMonomorphic(
|
| - BailoutId assignment_id,
|
| - HValue* object,
|
| - HValue* value,
|
| - SmallMapList* types,
|
| - Handle<String> name) {
|
| - // Use monomorphic store if property lookup results in the same field index
|
| - // for all maps. Requires special map check on the set of all handled maps.
|
| - if (types->length() > kMaxStorePolymorphism) return false;
|
| -
|
| - LookupResult lookup(isolate());
|
| - int count;
|
| - Representation representation = Representation::None();
|
| - HObjectAccess access = HObjectAccess::ForMap(); // initial value unused.
|
| - for (count = 0; count < types->length(); ++count) {
|
| - Handle<Map> map = types->at(count);
|
| - // Pass false to ignore transitions.
|
| - if (!ComputeStoreField(map, name, &lookup, false)) break;
|
| - ASSERT(!map->is_observed());
|
| -
|
| - HObjectAccess new_access = HObjectAccess::ForField(map, &lookup, name);
|
| - Representation new_representation = new_access.representation();
|
| -
|
| - if (count == 0) {
|
| - // First time through the loop; set access and representation.
|
| - access = new_access;
|
| - representation = new_representation;
|
| - } else if (!representation.IsCompatibleForStore(new_representation)) {
|
| - // Representations did not match.
|
| - break;
|
| - } else if (access.offset() != new_access.offset()) {
|
| - // Offsets did not match.
|
| - break;
|
| - } else if (access.IsInobject() != new_access.IsInobject()) {
|
| - // In-objectness did not match.
|
| - break;
|
| - }
|
| - }
|
| -
|
| - if (count != types->length()) return false;
|
| -
|
| - // Everything matched; can use monomorphic store.
|
| - BuildCheckHeapObject(object);
|
| - HCheckMaps* checked_object = Add<HCheckMaps>(object, types);
|
| - HInstruction* store;
|
| - CHECK_ALIVE_OR_RETURN(
|
| - store = BuildStoreNamedField(
|
| - checked_object, name, value, types->at(count - 1), &lookup),
|
| - true);
|
| - if (!ast_context()->IsEffect()) Push(value);
|
| - AddInstruction(store);
|
| - Add<HSimulate>(assignment_id);
|
| - if (!ast_context()->IsEffect()) Drop(1);
|
| - ast_context()->ReturnValue(value);
|
| - return true;
|
| -}
|
| -
|
| -
|
| -void HOptimizedGraphBuilder::HandlePolymorphicStoreNamedField(
|
| - BailoutId assignment_id,
|
| - HValue* object,
|
| - HValue* value,
|
| - SmallMapList* types,
|
| - Handle<String> name) {
|
| - if (TryStorePolymorphicAsMonomorphic(
|
| - assignment_id, object, value, types, name)) {
|
| - return;
|
| - }
|
| -
|
| - // TODO(ager): We should recognize when the prototype chains for different
|
| - // maps are identical. In that case we can avoid repeatedly generating the
|
| - // same prototype map checks.
|
| - int count = 0;
|
| - HBasicBlock* join = NULL;
|
| - for (int i = 0; i < types->length() && count < kMaxStorePolymorphism; ++i) {
|
| - Handle<Map> map = types->at(i);
|
| - LookupResult lookup(isolate());
|
| - if (ComputeStoreField(map, name, &lookup)) {
|
| - if (count == 0) {
|
| - BuildCheckHeapObject(object);
|
| - join = graph()->CreateBasicBlock();
|
| - }
|
| - ++count;
|
| - HBasicBlock* if_true = graph()->CreateBasicBlock();
|
| - HBasicBlock* if_false = graph()->CreateBasicBlock();
|
| - HCompareMap* compare = New<HCompareMap>(object, map, if_true, if_false);
|
| - FinishCurrentBlock(compare);
|
| -
|
| - set_current_block(if_true);
|
| - HInstruction* instr;
|
| - CHECK_ALIVE(instr = BuildStoreNamedField(
|
| - compare, name, value, map, &lookup));
|
| - // Goto will add the HSimulate for the store.
|
| - AddInstruction(instr);
|
| - if (!ast_context()->IsEffect()) Push(value);
|
| - Goto(join);
|
| -
|
| - set_current_block(if_false);
|
| - }
|
| - }
|
| -
|
| - // Finish up. Unconditionally deoptimize if we've handled all the maps we
|
| - // know about and do not want to handle ones we've never seen. Otherwise
|
| - // use a generic IC.
|
| - if (count == types->length() && FLAG_deoptimize_uncommon_cases) {
|
| - FinishExitWithHardDeoptimization("Unknown map in polymorphic store", join);
|
| - } else {
|
| - HInstruction* instr = BuildStoreNamedGeneric(object, name, value);
|
| - AddInstruction(instr);
|
| -
|
| - if (join != NULL) {
|
| - if (!ast_context()->IsEffect()) {
|
| - Push(value);
|
| - }
|
| - Goto(join);
|
| - } else {
|
| - // 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->HasObservableSideEffects()) {
|
| - if (ast_context()->IsEffect()) {
|
| - Add<HSimulate>(assignment_id, REMOVABLE_SIMULATE);
|
| - } else {
|
| - Push(value);
|
| - Add<HSimulate>(assignment_id, REMOVABLE_SIMULATE);
|
| - Drop(1);
|
| - }
|
| - }
|
| - return ast_context()->ReturnValue(value);
|
| - }
|
| - }
|
| -
|
| - ASSERT(join != NULL);
|
| - join->SetJoinId(assignment_id);
|
| - set_current_block(join);
|
| - if (!ast_context()->IsEffect()) {
|
| - ast_context()->ReturnValue(Pop());
|
| - }
|
| + if (join->HasPredecessor()) {
|
| + join->SetJoinId(ast_id);
|
| + set_current_block(join);
|
| + if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
|
| + } else {
|
| + set_current_block(NULL);
|
| + }
|
| }
|
|
|
|
|
| static bool ComputeReceiverTypes(Expression* expr,
|
| HValue* receiver,
|
| - SmallMapList** t) {
|
| + SmallMapList** t,
|
| + Zone* zone) {
|
| SmallMapList* types = expr->GetReceiverTypes();
|
| *t = types;
|
| bool monomorphic = expr->IsMonomorphic();
|
| @@ -5744,7 +5829,16 @@ static bool ComputeReceiverTypes(Expression* expr,
|
| types->FilterForPossibleTransitions(root_map);
|
| monomorphic = types->length() == 1;
|
| }
|
| - return monomorphic && CanInlinePropertyAccess(*types->first());
|
| + return monomorphic && CanInlinePropertyAccess(
|
| + IC::MapToType<Type>(types->first(), zone));
|
| +}
|
| +
|
| +
|
| +static bool AreStringTypes(SmallMapList* types) {
|
| + for (int i = 0; i < types->length(); i++) {
|
| + if (types->at(i)->instance_type() >= FIRST_NONSTRING_TYPE) return false;
|
| + }
|
| + return true;
|
| }
|
|
|
|
|
| @@ -5753,16 +5847,14 @@ void HOptimizedGraphBuilder::BuildStore(Expression* expr,
|
| BailoutId ast_id,
|
| BailoutId return_id,
|
| bool is_uninitialized) {
|
| - HValue* value = environment()->ExpressionStackAt(0);
|
| -
|
| if (!prop->key()->IsPropertyName()) {
|
| // Keyed store.
|
| + HValue* value = environment()->ExpressionStackAt(0);
|
| HValue* key = environment()->ExpressionStackAt(1);
|
| HValue* object = environment()->ExpressionStackAt(2);
|
| bool has_side_effects = false;
|
| HandleKeyedElementAccess(object, key, value, expr,
|
| - true, // is_store
|
| - &has_side_effects);
|
| + STORE, &has_side_effects);
|
| Drop(3);
|
| Push(value);
|
| Add<HSimulate>(return_id, REMOVABLE_SIMULATE);
|
| @@ -5770,50 +5862,16 @@ void HOptimizedGraphBuilder::BuildStore(Expression* expr,
|
| }
|
|
|
| // Named store.
|
| - HValue* object = environment()->ExpressionStackAt(1);
|
| -
|
| - if (is_uninitialized) {
|
| - Add<HDeoptimize>("Insufficient type feedback for property assignment",
|
| - Deoptimizer::SOFT);
|
| - }
|
| + HValue* value = Pop();
|
| + HValue* object = Pop();
|
|
|
| Literal* key = prop->key()->AsLiteral();
|
| Handle<String> name = Handle<String>::cast(key->value());
|
| ASSERT(!name.is_null());
|
|
|
| - HInstruction* instr = NULL;
|
| -
|
| - SmallMapList* types;
|
| - bool monomorphic = ComputeReceiverTypes(expr, object, &types);
|
| -
|
| - if (monomorphic) {
|
| - Handle<Map> map = types->first();
|
| - Handle<JSFunction> setter;
|
| - Handle<JSObject> holder;
|
| - if (LookupSetter(map, name, &setter, &holder)) {
|
| - AddCheckConstantFunction(holder, object, map);
|
| - if (FLAG_inline_accessors &&
|
| - TryInlineSetter(setter, ast_id, return_id, value)) {
|
| - return;
|
| - }
|
| - Drop(2);
|
| - Add<HPushArgument>(object);
|
| - Add<HPushArgument>(value);
|
| - instr = BuildCallConstantFunction(setter, 2);
|
| - } else {
|
| - Drop(2);
|
| - CHECK_ALIVE(instr = BuildStoreNamedMonomorphic(object,
|
| - name,
|
| - value,
|
| - map));
|
| - }
|
| - } else if (types != NULL && types->length() > 1) {
|
| - Drop(2);
|
| - return HandlePolymorphicStoreNamedField(ast_id, object, value, types, name);
|
| - } else {
|
| - Drop(2);
|
| - instr = BuildStoreNamedGeneric(object, name, value);
|
| - }
|
| + HInstruction* instr = BuildNamedAccess(STORE, ast_id, return_id, expr,
|
| + object, name, value, is_uninitialized);
|
| + if (instr == NULL) return;
|
|
|
| if (!ast_context()->IsEffect()) Push(value);
|
| AddInstruction(instr);
|
| @@ -5846,23 +5904,32 @@ void HOptimizedGraphBuilder::HandleGlobalVariableAssignment(
|
| HValue* value,
|
| BailoutId ast_id) {
|
| LookupResult lookup(isolate());
|
| - GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, true);
|
| + GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, STORE);
|
| if (type == kUseCell) {
|
| Handle<GlobalObject> global(current_info()->global_object());
|
| Handle<PropertyCell> cell(global->GetPropertyCell(&lookup));
|
| if (cell->type()->IsConstant()) {
|
| - IfBuilder builder(this);
|
| - HValue* constant = Add<HConstant>(cell->type()->AsConstant());
|
| - if (cell->type()->AsConstant()->IsNumber()) {
|
| - builder.If<HCompareNumericAndBranch>(value, constant, Token::EQ);
|
| + Handle<Object> constant = cell->type()->AsConstant();
|
| + if (value->IsConstant()) {
|
| + HConstant* c_value = HConstant::cast(value);
|
| + if (!constant.is_identical_to(c_value->handle(isolate()))) {
|
| + Add<HDeoptimize>("Constant global variable assignment",
|
| + Deoptimizer::EAGER);
|
| + }
|
| } else {
|
| - builder.If<HCompareObjectEqAndBranch>(value, constant);
|
| + HValue* c_constant = Add<HConstant>(constant);
|
| + IfBuilder builder(this);
|
| + if (constant->IsNumber()) {
|
| + builder.If<HCompareNumericAndBranch>(value, c_constant, Token::EQ);
|
| + } else {
|
| + builder.If<HCompareObjectEqAndBranch>(value, c_constant);
|
| + }
|
| + builder.Then();
|
| + builder.Else();
|
| + Add<HDeoptimize>("Constant global variable assignment",
|
| + Deoptimizer::EAGER);
|
| + builder.End();
|
| }
|
| - builder.Then();
|
| - builder.Else();
|
| - Add<HDeoptimize>("Constant global variable assignment",
|
| - Deoptimizer::EAGER);
|
| - builder.End();
|
| }
|
| HInstruction* instr =
|
| Add<HStoreGlobalCell>(value, cell, lookup.GetPropertyDetails());
|
| @@ -5870,7 +5937,9 @@ void HOptimizedGraphBuilder::HandleGlobalVariableAssignment(
|
| Add<HSimulate>(ast_id, REMOVABLE_SIMULATE);
|
| }
|
| } else {
|
| - HGlobalObject* global_object = Add<HGlobalObject>();
|
| + HValue* global_object = Add<HLoadNamedField>(
|
| + context(), static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForContextSlot(Context::GLOBAL_OBJECT_INDEX));
|
| HStoreNamedGeneric* instr =
|
| Add<HStoreNamedGeneric>(global_object, var->name(),
|
| value, function_strict_mode_flag());
|
| @@ -5976,7 +6045,8 @@ void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
|
|
| - Push(BuildBinaryOperation(operation, left, right));
|
| + Push(BuildBinaryOperation(operation, left, right, PUSH_BEFORE_SIMULATE));
|
| +
|
| BuildStore(expr, prop, expr->id(),
|
| expr->AssignmentId(), expr->IsUninitialized());
|
| } else {
|
| @@ -6127,8 +6197,10 @@ void HOptimizedGraphBuilder::VisitThrow(Throw* expr) {
|
| CHECK_ALIVE(VisitForValue(expr->exception()));
|
|
|
| HValue* value = environment()->Pop();
|
| - if (!FLAG_emit_opt_code_positions) SetSourcePosition(expr->position());
|
| - Add<HThrow>(value);
|
| + if (!FLAG_hydrogen_track_positions) SetSourcePosition(expr->position());
|
| + Add<HPushArgument>(value);
|
| + Add<HCallRuntime>(isolate()->factory()->empty_string(),
|
| + Runtime::FunctionForId(Runtime::kThrow), 1);
|
| Add<HSimulate>(expr->id());
|
|
|
| // If the throw definitely exits the function, we can finish with a dummy
|
| @@ -6140,27 +6212,6 @@ void HOptimizedGraphBuilder::VisitThrow(Throw* expr) {
|
| }
|
|
|
|
|
| -HLoadNamedField* HGraphBuilder::BuildLoadNamedField(HValue* object,
|
| - HObjectAccess access) {
|
| - if (FLAG_track_double_fields && access.representation().IsDouble()) {
|
| - // load the heap number
|
| - HLoadNamedField* heap_number = Add<HLoadNamedField>(
|
| - object, access.WithRepresentation(Representation::Tagged()));
|
| - heap_number->set_type(HType::HeapNumber());
|
| - // load the double value from it
|
| - return New<HLoadNamedField>(
|
| - heap_number, HObjectAccess::ForHeapNumberValue());
|
| - }
|
| - return New<HLoadNamedField>(object, access);
|
| -}
|
| -
|
| -
|
| -HInstruction* HGraphBuilder::AddLoadNamedField(HValue* object,
|
| - HObjectAccess access) {
|
| - return AddInstruction(BuildLoadNamedField(object, access));
|
| -}
|
| -
|
| -
|
| HInstruction* HGraphBuilder::AddLoadStringInstanceType(HValue* string) {
|
| if (string->IsConstant()) {
|
| HConstant* c_string = HConstant::cast(string);
|
| @@ -6168,9 +6219,10 @@ HInstruction* HGraphBuilder::AddLoadStringInstanceType(HValue* string) {
|
| return Add<HConstant>(c_string->StringValue()->map()->instance_type());
|
| }
|
| }
|
| - return AddLoadNamedField(
|
| - AddLoadNamedField(string, HObjectAccess::ForMap()),
|
| - HObjectAccess::ForMapInstanceType());
|
| + return Add<HLoadNamedField>(
|
| + Add<HLoadNamedField>(string, static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForMap()),
|
| + static_cast<HValue*>(NULL), HObjectAccess::ForMapInstanceType());
|
| }
|
|
|
|
|
| @@ -6181,26 +6233,42 @@ HInstruction* HGraphBuilder::AddLoadStringLength(HValue* string) {
|
| return Add<HConstant>(c_string->StringValue()->length());
|
| }
|
| }
|
| - return AddLoadNamedField(string, HObjectAccess::ForStringLength());
|
| + return Add<HLoadNamedField>(string, static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForStringLength());
|
| }
|
|
|
|
|
| -HInstruction* HOptimizedGraphBuilder::BuildLoadNamedGeneric(
|
| +HInstruction* HOptimizedGraphBuilder::BuildNamedGeneric(
|
| + PropertyAccessType access_type,
|
| HValue* object,
|
| Handle<String> name,
|
| - Property* expr) {
|
| - if (expr->IsUninitialized()) {
|
| - Add<HDeoptimize>("Insufficient type feedback for generic named load",
|
| + HValue* value,
|
| + bool is_uninitialized) {
|
| + if (is_uninitialized) {
|
| + Add<HDeoptimize>("Insufficient type feedback for generic named access",
|
| Deoptimizer::SOFT);
|
| }
|
| - return New<HLoadNamedGeneric>(object, name);
|
| + if (access_type == LOAD) {
|
| + return New<HLoadNamedGeneric>(object, name);
|
| + } else {
|
| + return New<HStoreNamedGeneric>(
|
| + object, name, value, function_strict_mode_flag());
|
| + }
|
| }
|
|
|
|
|
|
|
| -HInstruction* HOptimizedGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
|
| - HValue* key) {
|
| - return New<HLoadKeyedGeneric>(object, key);
|
| +HInstruction* HOptimizedGraphBuilder::BuildKeyedGeneric(
|
| + PropertyAccessType access_type,
|
| + HValue* object,
|
| + HValue* key,
|
| + HValue* value) {
|
| + if (access_type == LOAD) {
|
| + return New<HLoadKeyedGeneric>(object, key);
|
| + } else {
|
| + return New<HStoreKeyedGeneric>(
|
| + object, key, value, function_strict_mode_flag());
|
| + }
|
| }
|
|
|
|
|
| @@ -6226,15 +6294,15 @@ HInstruction* HOptimizedGraphBuilder::BuildMonomorphicElementAccess(
|
| HValue* val,
|
| HValue* dependency,
|
| Handle<Map> map,
|
| - bool is_store,
|
| + PropertyAccessType access_type,
|
| KeyedAccessStoreMode store_mode) {
|
| HCheckMaps* checked_object = Add<HCheckMaps>(object, map, top_info(),
|
| dependency);
|
| if (dependency) {
|
| - checked_object->ClearGVNFlag(kDependsOnElementsKind);
|
| + checked_object->ClearDependsOnFlag(kElementsKind);
|
| }
|
|
|
| - if (is_store && map->prototype()->IsJSObject()) {
|
| + if (access_type == STORE && map->prototype()->IsJSObject()) {
|
| // monomorphic stores need a prototype chain check because shape
|
| // changes could allow callbacks on elements in the chain that
|
| // aren't compatible with monomorphic keyed stores.
|
| @@ -6253,7 +6321,7 @@ HInstruction* HOptimizedGraphBuilder::BuildMonomorphicElementAccess(
|
| return BuildUncheckedMonomorphicElementAccess(
|
| checked_object, key, val,
|
| map->instance_type() == JS_ARRAY_TYPE,
|
| - map->elements_kind(), is_store,
|
| + map->elements_kind(), access_type,
|
| load_mode, store_mode);
|
| }
|
|
|
| @@ -6319,7 +6387,7 @@ HInstruction* HOptimizedGraphBuilder::TryBuildConsolidatedElementLoad(
|
| checked_object, key, val,
|
| most_general_consolidated_map->instance_type() == JS_ARRAY_TYPE,
|
| consolidated_elements_kind,
|
| - false, NEVER_RETURN_HOLE, STANDARD_STORE);
|
| + LOAD, NEVER_RETURN_HOLE, STANDARD_STORE);
|
| return instr;
|
| }
|
|
|
| @@ -6329,13 +6397,13 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess(
|
| HValue* key,
|
| HValue* val,
|
| SmallMapList* maps,
|
| - bool is_store,
|
| + PropertyAccessType access_type,
|
| KeyedAccessStoreMode store_mode,
|
| bool* has_side_effects) {
|
| *has_side_effects = false;
|
| BuildCheckHeapObject(object);
|
|
|
| - if (!is_store) {
|
| + if (access_type == LOAD) {
|
| HInstruction* consolidated_load =
|
| TryBuildConsolidatedElementLoad(object, key, val, maps);
|
| if (consolidated_load != NULL) {
|
| @@ -6388,15 +6456,14 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess(
|
| HInstruction* instr = NULL;
|
| if (untransitionable_map->has_slow_elements_kind() ||
|
| !untransitionable_map->IsJSObjectMap()) {
|
| - instr = AddInstruction(is_store ? BuildStoreKeyedGeneric(object, key, val)
|
| - : BuildLoadKeyedGeneric(object, key));
|
| + instr = AddInstruction(BuildKeyedGeneric(access_type, object, key, val));
|
| } else {
|
| instr = BuildMonomorphicElementAccess(
|
| - object, key, val, transition, untransitionable_map, is_store,
|
| + object, key, val, transition, untransitionable_map, access_type,
|
| store_mode);
|
| }
|
| *has_side_effects |= instr->HasObservableSideEffects();
|
| - return is_store ? NULL : instr;
|
| + return access_type == STORE ? NULL : instr;
|
| }
|
|
|
| HBasicBlock* join = graph()->CreateBasicBlock();
|
| @@ -6414,9 +6481,7 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess(
|
| set_current_block(this_map);
|
| HInstruction* access = NULL;
|
| if (IsDictionaryElementsKind(elements_kind)) {
|
| - access = is_store
|
| - ? AddInstruction(BuildStoreKeyedGeneric(object, key, val))
|
| - : AddInstruction(BuildLoadKeyedGeneric(object, key));
|
| + access = AddInstruction(BuildKeyedGeneric(access_type, object, key, val));
|
| } else {
|
| ASSERT(IsFastElementsKind(elements_kind) ||
|
| IsExternalArrayElementsKind(elements_kind));
|
| @@ -6425,14 +6490,14 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess(
|
| access = BuildUncheckedMonomorphicElementAccess(
|
| mapcompare, key, val,
|
| map->instance_type() == JS_ARRAY_TYPE,
|
| - elements_kind, is_store,
|
| + elements_kind, access_type,
|
| load_mode,
|
| store_mode);
|
| }
|
| *has_side_effects |= access->HasObservableSideEffects();
|
| // The caller will use has_side_effects and add a correct Simulate.
|
| access->SetFlag(HValue::kHasNoObservableSideEffects);
|
| - if (!is_store) {
|
| + if (access_type == LOAD) {
|
| Push(access);
|
| }
|
| NoObservableSideEffectsScope scope(this);
|
| @@ -6440,12 +6505,16 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess(
|
| set_current_block(other_map);
|
| }
|
|
|
| + // Ensure that we visited at least one map above that goes to join. This is
|
| + // necessary because FinishExitWithHardDeoptimization does an AbnormalExit
|
| + // rather than joining the join block. If this becomes an issue, insert a
|
| + // generic access in the case length() == 0.
|
| + ASSERT(join->predecessors()->length() > 0);
|
| // Deopt if none of the cases matched.
|
| NoObservableSideEffectsScope scope(this);
|
| - FinishExitWithHardDeoptimization("Unknown map in polymorphic element access",
|
| - join);
|
| + FinishExitWithHardDeoptimization("Unknown map in polymorphic element access");
|
| set_current_block(join);
|
| - return is_store ? NULL : Pop();
|
| + return access_type == STORE ? NULL : Pop();
|
| }
|
|
|
|
|
| @@ -6454,16 +6523,17 @@ HValue* HOptimizedGraphBuilder::HandleKeyedElementAccess(
|
| HValue* key,
|
| HValue* val,
|
| Expression* expr,
|
| - bool is_store,
|
| + PropertyAccessType access_type,
|
| bool* has_side_effects) {
|
| ASSERT(!expr->IsPropertyName());
|
| HInstruction* instr = NULL;
|
|
|
| SmallMapList* types;
|
| - bool monomorphic = ComputeReceiverTypes(expr, obj, &types);
|
| + bool monomorphic = ComputeReceiverTypes(expr, obj, &types, zone());
|
|
|
| bool force_generic = false;
|
| - if (is_store && (monomorphic || (types != NULL && !types->is_empty()))) {
|
| + if (access_type == STORE &&
|
| + (monomorphic || (types != NULL && !types->is_empty()))) {
|
| // Stores can't be mono/polymorphic if their prototype chain has dictionary
|
| // elements. However a receiver map that has dictionary elements itself
|
| // should be left to normal mono/poly behavior (the other maps may benefit
|
| @@ -6480,53 +6550,37 @@ HValue* HOptimizedGraphBuilder::HandleKeyedElementAccess(
|
|
|
| if (monomorphic) {
|
| Handle<Map> map = types->first();
|
| - if (map->has_slow_elements_kind()) {
|
| - instr = is_store ? BuildStoreKeyedGeneric(obj, key, val)
|
| - : BuildLoadKeyedGeneric(obj, key);
|
| - AddInstruction(instr);
|
| + if (map->has_slow_elements_kind() || !map->IsJSObjectMap()) {
|
| + instr = AddInstruction(BuildKeyedGeneric(access_type, obj, key, val));
|
| } else {
|
| BuildCheckHeapObject(obj);
|
| instr = BuildMonomorphicElementAccess(
|
| - obj, key, val, NULL, map, is_store, expr->GetStoreMode());
|
| + obj, key, val, NULL, map, access_type, expr->GetStoreMode());
|
| }
|
| } else if (!force_generic && (types != NULL && !types->is_empty())) {
|
| return HandlePolymorphicElementAccess(
|
| - obj, key, val, types, is_store,
|
| + obj, key, val, types, access_type,
|
| expr->GetStoreMode(), has_side_effects);
|
| } else {
|
| - if (is_store) {
|
| + if (access_type == STORE) {
|
| if (expr->IsAssignment() &&
|
| expr->AsAssignment()->HasNoTypeInformation()) {
|
| Add<HDeoptimize>("Insufficient type feedback for keyed store",
|
| Deoptimizer::SOFT);
|
| }
|
| - instr = BuildStoreKeyedGeneric(obj, key, val);
|
| } else {
|
| if (expr->AsProperty()->HasNoTypeInformation()) {
|
| Add<HDeoptimize>("Insufficient type feedback for keyed load",
|
| Deoptimizer::SOFT);
|
| }
|
| - instr = BuildLoadKeyedGeneric(obj, key);
|
| }
|
| - AddInstruction(instr);
|
| + instr = AddInstruction(BuildKeyedGeneric(access_type, obj, key, val));
|
| }
|
| *has_side_effects = instr->HasObservableSideEffects();
|
| return instr;
|
| }
|
|
|
|
|
| -HInstruction* HOptimizedGraphBuilder::BuildStoreKeyedGeneric(
|
| - HValue* object,
|
| - HValue* key,
|
| - HValue* value) {
|
| - return New<HStoreKeyedGeneric>(
|
| - object,
|
| - key,
|
| - value,
|
| - function_strict_mode_flag());
|
| -}
|
| -
|
| -
|
| void HOptimizedGraphBuilder::EnsureArgumentsArePushedForAccess() {
|
| // Outermost function already has arguments on the stack.
|
| if (function_state()->outer() == NULL) return;
|
| @@ -6604,6 +6658,45 @@ bool HOptimizedGraphBuilder::TryArgumentsAccess(Property* expr) {
|
| }
|
|
|
|
|
| +HInstruction* HOptimizedGraphBuilder::BuildNamedAccess(
|
| + PropertyAccessType access,
|
| + BailoutId ast_id,
|
| + BailoutId return_id,
|
| + Expression* expr,
|
| + HValue* object,
|
| + Handle<String> name,
|
| + HValue* value,
|
| + bool is_uninitialized) {
|
| + SmallMapList* types;
|
| + ComputeReceiverTypes(expr, object, &types, zone());
|
| + ASSERT(types != NULL);
|
| +
|
| + if (types->length() > 0) {
|
| + PropertyAccessInfo info(this, access, ToType(types->first()), name);
|
| + if (!info.CanAccessAsMonomorphic(types)) {
|
| + HandlePolymorphicNamedFieldAccess(
|
| + access, ast_id, return_id, object, value, types, name);
|
| + return NULL;
|
| + }
|
| +
|
| + HValue* checked_object;
|
| + // Type::Number() is only supported by polymorphic load/call handling.
|
| + ASSERT(!info.type()->Is(Type::Number()));
|
| + BuildCheckHeapObject(object);
|
| + if (AreStringTypes(types)) {
|
| + checked_object =
|
| + Add<HCheckInstanceType>(object, HCheckInstanceType::IS_STRING);
|
| + } else {
|
| + checked_object = Add<HCheckMaps>(object, types);
|
| + }
|
| + return BuildMonomorphicAccess(
|
| + &info, object, checked_object, value, ast_id, return_id);
|
| + }
|
| +
|
| + return BuildNamedGeneric(access, object, name, value, is_uninitialized);
|
| +}
|
| +
|
| +
|
| void HOptimizedGraphBuilder::PushLoad(Property* expr,
|
| HValue* object,
|
| HValue* key) {
|
| @@ -6614,14 +6707,6 @@ void HOptimizedGraphBuilder::PushLoad(Property* expr,
|
| }
|
|
|
|
|
| -static bool AreStringTypes(SmallMapList* types) {
|
| - for (int i = 0; i < types->length(); i++) {
|
| - if (types->at(i)->instance_type() >= FIRST_NONSTRING_TYPE) return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -
|
| void HOptimizedGraphBuilder::BuildLoad(Property* expr,
|
| BailoutId ast_id) {
|
| HInstruction* instr = NULL;
|
| @@ -6641,32 +6726,10 @@ void HOptimizedGraphBuilder::BuildLoad(Property* expr,
|
| Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
|
| HValue* object = Pop();
|
|
|
| - SmallMapList* types;
|
| - ComputeReceiverTypes(expr, object, &types);
|
| - ASSERT(types != NULL);
|
| -
|
| - if (types->length() > 0) {
|
| - PropertyAccessInfo info(isolate(), types->first(), name);
|
| - if (!info.CanLoadAsMonomorphic(types)) {
|
| - return HandlePolymorphicLoadNamedField(
|
| - ast_id, expr->LoadId(), object, types, name);
|
| - }
|
| -
|
| - BuildCheckHeapObject(object);
|
| - HInstruction* checked_object;
|
| - if (AreStringTypes(types)) {
|
| - checked_object =
|
| - Add<HCheckInstanceType>(object, HCheckInstanceType::IS_STRING);
|
| - } else {
|
| - checked_object = Add<HCheckMaps>(object, types);
|
| - }
|
| - instr = BuildLoadMonomorphic(
|
| - &info, object, checked_object, ast_id, expr->LoadId());
|
| - if (instr == NULL) return;
|
| - if (instr->IsLinked()) return ast_context()->ReturnValue(instr);
|
| - } else {
|
| - instr = BuildLoadNamedGeneric(object, name, expr);
|
| - }
|
| + instr = BuildNamedAccess(LOAD, ast_id, expr->LoadId(), expr,
|
| + object, name, NULL, expr->IsUninitialized());
|
| + if (instr == NULL) return;
|
| + if (instr->IsLinked()) return ast_context()->ReturnValue(instr);
|
|
|
| } else {
|
| HValue* key = Pop();
|
| @@ -6674,9 +6737,7 @@ void HOptimizedGraphBuilder::BuildLoad(Property* expr,
|
|
|
| bool has_side_effects = false;
|
| HValue* load = HandleKeyedElementAccess(
|
| - obj, key, NULL, expr,
|
| - false, // is_store
|
| - &has_side_effects);
|
| + obj, key, NULL, expr, LOAD, &has_side_effects);
|
| if (has_side_effects) {
|
| if (ast_context()->IsEffect()) {
|
| Add<HSimulate>(ast_id, REMOVABLE_SIMULATE);
|
| @@ -6722,7 +6783,7 @@ HInstruction* HGraphBuilder::BuildConstantMapCheck(Handle<JSObject> constant,
|
| AddInstruction(constant_value);
|
| HCheckMaps* check =
|
| Add<HCheckMaps>(constant_value, handle(constant->map()), info);
|
| - check->ClearGVNFlag(kDependsOnElementsKind);
|
| + check->ClearDependsOnFlag(kElementsKind);
|
| return check;
|
| }
|
|
|
| @@ -6749,18 +6810,6 @@ void HOptimizedGraphBuilder::AddCheckPrototypeMaps(Handle<JSObject> holder,
|
| }
|
|
|
|
|
| -void HOptimizedGraphBuilder::AddCheckConstantFunction(
|
| - Handle<JSObject> holder,
|
| - HValue* receiver,
|
| - Handle<Map> receiver_map) {
|
| - // Constant functions have the nice property that the map will change if they
|
| - // are overwritten. Therefore it is enough to check the map of the holder and
|
| - // its prototypes.
|
| - AddCheckMap(receiver, receiver_map);
|
| - AddCheckPrototypeMaps(holder, receiver_map);
|
| -}
|
| -
|
| -
|
| HInstruction* HOptimizedGraphBuilder::NewPlainFunctionCall(
|
| HValue* fun, int argument_count, bool pass_argument_count) {
|
| return New<HCallJSFunction>(
|
| @@ -6801,10 +6850,14 @@ HInstruction* HOptimizedGraphBuilder::BuildCallConstantFunction(
|
| bool can_invoke_directly =
|
| dont_adapt_arguments || formal_parameter_count == arity;
|
| if (can_invoke_directly) {
|
| + if (jsfun.is_identical_to(current_info()->closure())) {
|
| + graph()->MarkRecursive();
|
| + }
|
| return NewPlainFunctionCall(target, argument_count, dont_adapt_arguments);
|
| } else {
|
| HValue* param_count_value = Add<HConstant>(formal_parameter_count);
|
| - HValue* context = Add<HLoadNamedField>(target,
|
| + HValue* context = Add<HLoadNamedField>(
|
| + target, static_cast<HValue*>(NULL),
|
| HObjectAccess::ForFunctionContextPointer());
|
| return NewArgumentAdaptorCall(target, context,
|
| argument_count, param_count_value);
|
| @@ -6814,147 +6867,56 @@ HInstruction* HOptimizedGraphBuilder::BuildCallConstantFunction(
|
| }
|
|
|
|
|
| -HInstruction* HOptimizedGraphBuilder::NewCallNamed(
|
| - Handle<String> name, int argument_count) {
|
| - CallInterfaceDescriptor* descriptor =
|
| - isolate()->call_descriptor(Isolate::NamedCall);
|
| - HValue* op_vals[] = { context(), Add<HConstant>(name) };
|
| - int arity = argument_count - 1;
|
| - Handle<Code> ic = isolate()->stub_cache()->ComputeCallInitialize(arity);
|
| -
|
| - return New<HCallWithDescriptor>(
|
| - Add<HConstant>(ic), argument_count, descriptor,
|
| - Vector<HValue*>(op_vals, descriptor->environment_length()));
|
| -}
|
| -
|
| -
|
| -HInstruction* HOptimizedGraphBuilder::NewCallKeyed(
|
| - HValue* key, int argument_count) {
|
| - CallInterfaceDescriptor* descriptor =
|
| - isolate()->call_descriptor(Isolate::KeyedCall);
|
| - HValue* op_vals[] = { context(), key };
|
| - int arity = argument_count - 1;
|
| - Handle<Code> ic = isolate()->stub_cache()->ComputeKeyedCallInitialize(arity);
|
| -
|
| - return New<HCallWithDescriptor>(
|
| - Add<HConstant>(ic), argument_count, descriptor,
|
| - Vector<HValue*>(op_vals, descriptor->environment_length()));
|
| -}
|
| -
|
| -class FunctionSorter {
|
| - public:
|
| - FunctionSorter() : index_(0), ticks_(0), ast_length_(0), src_length_(0) { }
|
| - FunctionSorter(int index, int ticks, int ast_length, int src_length)
|
| - : index_(index),
|
| - ticks_(ticks),
|
| - ast_length_(ast_length),
|
| - src_length_(src_length) { }
|
| -
|
| - int index() const { return index_; }
|
| - int ticks() const { return ticks_; }
|
| - int ast_length() const { return ast_length_; }
|
| - int src_length() const { return src_length_; }
|
| -
|
| - private:
|
| - int index_;
|
| - int ticks_;
|
| - int ast_length_;
|
| - int src_length_;
|
| -};
|
| -
|
| -
|
| -inline bool operator<(const FunctionSorter& lhs, const FunctionSorter& rhs) {
|
| - int diff = lhs.ticks() - rhs.ticks();
|
| - if (diff != 0) return diff > 0;
|
| - diff = lhs.ast_length() - rhs.ast_length();
|
| - if (diff != 0) return diff < 0;
|
| - return lhs.src_length() < rhs.src_length();
|
| -}
|
| -
|
| -
|
| -bool HOptimizedGraphBuilder::TryCallPolymorphicAsMonomorphic(
|
| - Call* expr,
|
| - HValue* receiver,
|
| - SmallMapList* types,
|
| - Handle<String> name) {
|
| - if (types->length() > kMaxCallPolymorphism) return false;
|
| -
|
| - PropertyAccessInfo info(isolate(), types->at(0), name);
|
| - if (!info.CanLoadAsMonomorphic(types)) return false;
|
| - if (!expr->ComputeTarget(info.map(), name)) return false;
|
| -
|
| - BuildCheckHeapObject(receiver);
|
| - Add<HCheckMaps>(receiver, types);
|
| - AddCheckPrototypeMaps(expr->holder(), info.map());
|
| - if (FLAG_trace_inlining) {
|
| - Handle<JSFunction> caller = current_info()->closure();
|
| - SmartArrayPointer<char> caller_name =
|
| - caller->shared()->DebugName()->ToCString();
|
| - PrintF("Trying to inline the polymorphic call to %s from %s\n",
|
| - name->ToCString().get(), caller_name.get());
|
| - }
|
| -
|
| - if (!TryInlineCall(expr)) {
|
| - int argument_count = expr->arguments()->length() + 1; // Includes receiver.
|
| - HInstruction* call = BuildCallConstantFunction(
|
| - expr->target(), argument_count);
|
| - PushArgumentsFromEnvironment(argument_count);
|
| - AddInstruction(call);
|
| - if (!ast_context()->IsEffect()) Push(call);
|
| - Add<HSimulate>(expr->id(), REMOVABLE_SIMULATE);
|
| - if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop());
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -
|
| void HOptimizedGraphBuilder::HandlePolymorphicCallNamed(
|
| Call* expr,
|
| HValue* receiver,
|
| SmallMapList* types,
|
| Handle<String> name) {
|
| - if (TryCallPolymorphicAsMonomorphic(expr, receiver, types, name)) return;
|
| -
|
| int argument_count = expr->arguments()->length() + 1; // Includes receiver.
|
| - HBasicBlock* join = NULL;
|
| - FunctionSorter order[kMaxCallPolymorphism];
|
| - int ordered_functions = 0;
|
| -
|
| - Handle<Map> initial_string_map(
|
| - isolate()->native_context()->string_function()->initial_map());
|
| - Handle<Map> string_marker_map(
|
| - JSObject::cast(initial_string_map->prototype())->map());
|
| - Handle<Map> initial_number_map(
|
| - isolate()->native_context()->number_function()->initial_map());
|
| - Handle<Map> number_marker_map(
|
| - JSObject::cast(initial_number_map->prototype())->map());
|
| - Handle<Map> heap_number_map = isolate()->factory()->heap_number_map();
|
| + int order[kMaxCallPolymorphism];
|
|
|
| bool handle_smi = false;
|
| + bool handled_string = false;
|
| + int ordered_functions = 0;
|
|
|
| for (int i = 0;
|
| i < types->length() && ordered_functions < kMaxCallPolymorphism;
|
| ++i) {
|
| - Handle<Map> map = types->at(i);
|
| - if (expr->ComputeTarget(map, name)) {
|
| - if (map.is_identical_to(number_marker_map)) handle_smi = true;
|
| - order[ordered_functions++] =
|
| - FunctionSorter(i,
|
| - expr->target()->shared()->profiler_ticks(),
|
| - InliningAstSize(expr->target()),
|
| - expr->target()->shared()->SourceSize());
|
| + PropertyAccessInfo info(this, LOAD, ToType(types->at(i)), name);
|
| + if (info.CanAccessMonomorphic() &&
|
| + info.lookup()->IsConstant() &&
|
| + info.constant()->IsJSFunction()) {
|
| + if (info.type()->Is(Type::String())) {
|
| + if (handled_string) continue;
|
| + handled_string = true;
|
| + }
|
| + Handle<JSFunction> target = Handle<JSFunction>::cast(info.constant());
|
| + if (info.type()->Is(Type::Number())) {
|
| + handle_smi = true;
|
| + }
|
| + expr->set_target(target);
|
| + order[ordered_functions++] = i;
|
| }
|
| }
|
|
|
| - std::sort(order, order + ordered_functions);
|
| -
|
| HBasicBlock* number_block = NULL;
|
| + HBasicBlock* join = NULL;
|
| + handled_string = false;
|
| + int count = 0;
|
|
|
| for (int fn = 0; fn < ordered_functions; ++fn) {
|
| - int i = order[fn].index();
|
| - Handle<Map> map = types->at(i);
|
| - if (fn == 0) {
|
| + int i = order[fn];
|
| + PropertyAccessInfo info(this, LOAD, ToType(types->at(i)), name);
|
| + if (info.type()->Is(Type::String())) {
|
| + if (handled_string) continue;
|
| + handled_string = true;
|
| + }
|
| + // Reloads the target.
|
| + info.CanAccessMonomorphic();
|
| + Handle<JSFunction> target = Handle<JSFunction>::cast(info.constant());
|
| +
|
| + expr->set_target(target);
|
| + if (count == 0) {
|
| // Only needed once.
|
| join = graph()->CreateBasicBlock();
|
| if (handle_smi) {
|
| @@ -6963,43 +6925,44 @@ void HOptimizedGraphBuilder::HandlePolymorphicCallNamed(
|
| number_block = graph()->CreateBasicBlock();
|
| FinishCurrentBlock(New<HIsSmiAndBranch>(
|
| receiver, empty_smi_block, not_smi_block));
|
| - Goto(empty_smi_block, number_block);
|
| + GotoNoSimulate(empty_smi_block, number_block);
|
| set_current_block(not_smi_block);
|
| } else {
|
| BuildCheckHeapObject(receiver);
|
| }
|
| }
|
| + ++count;
|
| HBasicBlock* if_true = graph()->CreateBasicBlock();
|
| HBasicBlock* if_false = graph()->CreateBasicBlock();
|
| HUnaryControlInstruction* compare;
|
|
|
| - if (handle_smi && map.is_identical_to(number_marker_map)) {
|
| + Handle<Map> map = info.map();
|
| + if (info.type()->Is(Type::Number())) {
|
| + Handle<Map> heap_number_map = isolate()->factory()->heap_number_map();
|
| compare = New<HCompareMap>(receiver, heap_number_map, if_true, if_false);
|
| - map = initial_number_map;
|
| - expr->set_number_check(
|
| - Handle<JSObject>(JSObject::cast(map->prototype())));
|
| - } else if (map.is_identical_to(string_marker_map)) {
|
| + } else if (info.type()->Is(Type::String())) {
|
| compare = New<HIsStringAndBranch>(receiver, if_true, if_false);
|
| - map = initial_string_map;
|
| - expr->set_string_check(
|
| - Handle<JSObject>(JSObject::cast(map->prototype())));
|
| } else {
|
| compare = New<HCompareMap>(receiver, map, if_true, if_false);
|
| - expr->set_map_check();
|
| }
|
| -
|
| FinishCurrentBlock(compare);
|
|
|
| - if (expr->check_type() == NUMBER_CHECK) {
|
| - Goto(if_true, number_block);
|
| + if (info.type()->Is(Type::Number())) {
|
| + GotoNoSimulate(if_true, number_block);
|
| if_true = number_block;
|
| - number_block->SetJoinId(expr->id());
|
| }
|
| +
|
| set_current_block(if_true);
|
|
|
| - expr->ComputeTarget(map, name);
|
| - AddCheckPrototypeMaps(expr->holder(), map);
|
| - if (FLAG_trace_inlining && FLAG_polymorphic_inlining) {
|
| + AddCheckPrototypeMaps(info.holder(), map);
|
| +
|
| + HValue* function = Add<HConstant>(expr->target());
|
| + environment()->SetExpressionStackAt(0, function);
|
| + Push(receiver);
|
| + CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| + bool needs_wrapping = NeedsWrappingFor(info.type(), target);
|
| + bool try_inline = FLAG_polymorphic_inlining && !needs_wrapping;
|
| + if (FLAG_trace_inlining && try_inline) {
|
| Handle<JSFunction> caller = current_info()->closure();
|
| SmartArrayPointer<char> caller_name =
|
| caller->shared()->DebugName()->ToCString();
|
| @@ -7007,15 +6970,22 @@ void HOptimizedGraphBuilder::HandlePolymorphicCallNamed(
|
| name->ToCString().get(),
|
| caller_name.get());
|
| }
|
| - if (FLAG_polymorphic_inlining && TryInlineCall(expr)) {
|
| + if (try_inline && TryInlineCall(expr)) {
|
| // Trying to inline will signal that we should bailout from the
|
| // entire compilation by setting stack overflow on the visitor.
|
| if (HasStackOverflow()) return;
|
| } else {
|
| - HInstruction* call = BuildCallConstantFunction(
|
| - expr->target(), argument_count);
|
| + // Since HWrapReceiver currently cannot actually wrap numbers and strings,
|
| + // use the regular CallFunctionStub for method calls to wrap the receiver.
|
| + // TODO(verwaest): Support creation of value wrappers directly in
|
| + // HWrapReceiver.
|
| + HInstruction* call = needs_wrapping
|
| + ? NewUncasted<HCallFunction>(
|
| + function, argument_count, WRAP_AND_CALL)
|
| + : BuildCallConstantFunction(target, argument_count);
|
| PushArgumentsFromEnvironment(argument_count);
|
| AddInstruction(call);
|
| + Drop(1); // Drop the function.
|
| if (!ast_context()->IsEffect()) Push(call);
|
| }
|
|
|
| @@ -7027,16 +6997,28 @@ void HOptimizedGraphBuilder::HandlePolymorphicCallNamed(
|
| // know about and do not want to handle ones we've never seen. Otherwise
|
| // use a generic IC.
|
| if (ordered_functions == types->length() && FLAG_deoptimize_uncommon_cases) {
|
| - // Because the deopt may be the only path in the polymorphic call, make sure
|
| - // that the environment stack matches the depth on deopt that it otherwise
|
| - // would have had after a successful call.
|
| - Drop(argument_count);
|
| - if (!ast_context()->IsEffect()) Push(graph()->GetConstant0());
|
| - FinishExitWithHardDeoptimization("Unknown map in polymorphic call", join);
|
| + FinishExitWithHardDeoptimization("Unknown map in polymorphic call");
|
| } else {
|
| - HInstruction* call = NewCallNamed(name, argument_count);
|
| + Property* prop = expr->expression()->AsProperty();
|
| + HInstruction* function = BuildNamedGeneric(
|
| + LOAD, receiver, name, NULL, prop->IsUninitialized());
|
| + AddInstruction(function);
|
| + Push(function);
|
| + AddSimulate(prop->LoadId(), REMOVABLE_SIMULATE);
|
| +
|
| + environment()->SetExpressionStackAt(1, function);
|
| + environment()->SetExpressionStackAt(0, receiver);
|
| + CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| +
|
| + CallFunctionFlags flags = receiver->type().IsJSObject()
|
| + ? NO_CALL_FUNCTION_FLAGS : CALL_AS_METHOD;
|
| + HInstruction* call = New<HCallFunction>(
|
| + function, argument_count, flags);
|
| +
|
| PushArgumentsFromEnvironment(argument_count);
|
|
|
| + Drop(1); // Function.
|
| +
|
| if (join != NULL) {
|
| AddInstruction(call);
|
| if (!ast_context()->IsEffect()) Push(call);
|
| @@ -7123,7 +7105,8 @@ bool HOptimizedGraphBuilder::TryInline(Handle<JSFunction> target,
|
| HValue* implicit_return_value,
|
| BailoutId ast_id,
|
| BailoutId return_id,
|
| - InliningKind inlining_kind) {
|
| + InliningKind inlining_kind,
|
| + HSourcePosition position) {
|
| int nodes_added = InliningAstSize(target);
|
| if (nodes_added == kNotInlinable) return false;
|
|
|
| @@ -7255,11 +7238,13 @@ bool HOptimizedGraphBuilder::TryInline(Handle<JSFunction> target,
|
| ASSERT(target_shared->has_deoptimization_support());
|
| AstTyper::Run(&target_info);
|
|
|
| + int function_id = graph()->TraceInlinedFunction(target_shared, position);
|
| +
|
| // Save the pending call context. Set up new one for the inlined function.
|
| // The function state is new-allocated because we need to delete it
|
| // in two different places.
|
| FunctionState* target_state = new FunctionState(
|
| - this, &target_info, inlining_kind);
|
| + this, &target_info, inlining_kind, function_id);
|
|
|
| HConstant* undefined = graph()->GetConstantUndefined();
|
|
|
| @@ -7400,13 +7385,14 @@ bool HOptimizedGraphBuilder::TryInline(Handle<JSFunction> target,
|
| }
|
|
|
|
|
| -bool HOptimizedGraphBuilder::TryInlineCall(Call* expr, bool drop_extra) {
|
| +bool HOptimizedGraphBuilder::TryInlineCall(Call* expr) {
|
| return TryInline(expr->target(),
|
| expr->arguments()->length(),
|
| NULL,
|
| expr->id(),
|
| expr->ReturnId(),
|
| - drop_extra ? DROP_EXTRA_ON_RETURN : NORMAL_RETURN);
|
| + NORMAL_RETURN,
|
| + ScriptPositionToSourcePosition(expr->position()));
|
| }
|
|
|
|
|
| @@ -7417,31 +7403,38 @@ bool HOptimizedGraphBuilder::TryInlineConstruct(CallNew* expr,
|
| implicit_return_value,
|
| expr->id(),
|
| expr->ReturnId(),
|
| - CONSTRUCT_CALL_RETURN);
|
| + CONSTRUCT_CALL_RETURN,
|
| + ScriptPositionToSourcePosition(expr->position()));
|
| }
|
|
|
|
|
| bool HOptimizedGraphBuilder::TryInlineGetter(Handle<JSFunction> getter,
|
| + Handle<Map> receiver_map,
|
| BailoutId ast_id,
|
| BailoutId return_id) {
|
| + if (TryInlineApiGetter(getter, receiver_map, ast_id)) return true;
|
| return TryInline(getter,
|
| 0,
|
| NULL,
|
| ast_id,
|
| return_id,
|
| - GETTER_CALL_RETURN);
|
| + GETTER_CALL_RETURN,
|
| + source_position());
|
| }
|
|
|
|
|
| bool HOptimizedGraphBuilder::TryInlineSetter(Handle<JSFunction> setter,
|
| + Handle<Map> receiver_map,
|
| BailoutId id,
|
| BailoutId assignment_id,
|
| HValue* implicit_return_value) {
|
| + if (TryInlineApiSetter(setter, receiver_map, id)) return true;
|
| return TryInline(setter,
|
| 1,
|
| implicit_return_value,
|
| id, assignment_id,
|
| - SETTER_CALL_RETURN);
|
| + SETTER_CALL_RETURN,
|
| + source_position());
|
| }
|
|
|
|
|
| @@ -7453,12 +7446,12 @@ bool HOptimizedGraphBuilder::TryInlineApply(Handle<JSFunction> function,
|
| NULL,
|
| expr->id(),
|
| expr->ReturnId(),
|
| - NORMAL_RETURN);
|
| + NORMAL_RETURN,
|
| + ScriptPositionToSourcePosition(expr->position()));
|
| }
|
|
|
|
|
| -bool HOptimizedGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr,
|
| - bool drop_extra) {
|
| +bool HOptimizedGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr) {
|
| if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
|
| BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
|
| switch (id) {
|
| @@ -7470,11 +7463,11 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr,
|
| case kMathAbs:
|
| case kMathSqrt:
|
| case kMathLog:
|
| + case kMathClz32:
|
| if (expr->arguments()->length() == 1) {
|
| HValue* argument = Pop();
|
| - Drop(1); // Receiver.
|
| + Drop(2); // Receiver and function.
|
| HInstruction* op = NewUncasted<HUnaryMathOperation>(argument, id);
|
| - if (drop_extra) Drop(1); // Optionally drop the function.
|
| ast_context()->ReturnInstruction(op, expr->id());
|
| return true;
|
| }
|
| @@ -7483,9 +7476,8 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr,
|
| if (expr->arguments()->length() == 2) {
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
| - Drop(1); // Receiver.
|
| + Drop(2); // Receiver and function.
|
| HInstruction* op = HMul::NewImul(zone(), context(), left, right);
|
| - if (drop_extra) Drop(1); // Optionally drop the function.
|
| ast_context()->ReturnInstruction(op, expr->id());
|
| return true;
|
| }
|
| @@ -7501,9 +7493,7 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr,
|
| bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
|
| Call* expr,
|
| HValue* receiver,
|
| - Handle<Map> receiver_map,
|
| - CheckType check_type) {
|
| - ASSERT(check_type != RECEIVER_MAP_CHECK || !receiver_map.is_null());
|
| + Handle<Map> receiver_map) {
|
| // Try to inline calls like Math.* as operations in the calling function.
|
| if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
|
| BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
|
| @@ -7511,13 +7501,10 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
|
| switch (id) {
|
| case kStringCharCodeAt:
|
| case kStringCharAt:
|
| - if (argument_count == 2 && check_type == STRING_CHECK) {
|
| + if (argument_count == 2) {
|
| HValue* index = Pop();
|
| HValue* string = Pop();
|
| - ASSERT(!expr->holder().is_null());
|
| - BuildCheckPrototypeMaps(Call::GetPrototypeForPrimitiveCheck(
|
| - STRING_CHECK, expr->holder()->GetIsolate()),
|
| - expr->holder());
|
| + Drop(1); // Function.
|
| HInstruction* char_code =
|
| BuildStringCharCodeAt(string, index);
|
| if (id == kStringCharCodeAt) {
|
| @@ -7531,10 +7518,9 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
|
| }
|
| break;
|
| case kStringFromCharCode:
|
| - if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
|
| - AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
|
| + if (argument_count == 2) {
|
| HValue* argument = Pop();
|
| - Drop(1); // Receiver.
|
| + Drop(2); // Receiver and function.
|
| HInstruction* result = NewUncasted<HStringCharFromCode>(argument);
|
| ast_context()->ReturnInstruction(result, expr->id());
|
| return true;
|
| @@ -7548,21 +7534,20 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
|
| case kMathAbs:
|
| case kMathSqrt:
|
| case kMathLog:
|
| - if (argument_count == 2 && check_type == RECEIVER_MAP_CHECK) {
|
| - AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
|
| + case kMathClz32:
|
| + if (argument_count == 2) {
|
| HValue* argument = Pop();
|
| - Drop(1); // Receiver.
|
| + Drop(2); // Receiver and function.
|
| HInstruction* op = NewUncasted<HUnaryMathOperation>(argument, id);
|
| ast_context()->ReturnInstruction(op, expr->id());
|
| return true;
|
| }
|
| break;
|
| case kMathPow:
|
| - if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
|
| - AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
|
| + if (argument_count == 3) {
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
| - Pop(); // Pop receiver.
|
| + Drop(2); // Receiver and function.
|
| HInstruction* result = NULL;
|
| // Use sqrt() if exponent is 0.5 or -0.5.
|
| if (right->IsConstant() && HConstant::cast(right)->HasDoubleValue()) {
|
| @@ -7591,11 +7576,10 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
|
| break;
|
| case kMathMax:
|
| case kMathMin:
|
| - if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
|
| - AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
|
| + if (argument_count == 3) {
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
| - Drop(1); // Receiver.
|
| + Drop(2); // Receiver and function.
|
| HMathMinMax::Operation op = (id == kMathMin) ? HMathMinMax::kMathMin
|
| : HMathMinMax::kMathMax;
|
| HInstruction* result = NewUncasted<HMathMinMax>(left, right, op);
|
| @@ -7604,16 +7588,110 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
|
| }
|
| break;
|
| case kMathImul:
|
| - if (argument_count == 3 && check_type == RECEIVER_MAP_CHECK) {
|
| - AddCheckConstantFunction(expr->holder(), receiver, receiver_map);
|
| + if (argument_count == 3) {
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
| - Drop(1); // Receiver.
|
| + Drop(2); // Receiver and function.
|
| HInstruction* result = HMul::NewImul(zone(), context(), left, right);
|
| ast_context()->ReturnInstruction(result, expr->id());
|
| return true;
|
| }
|
| break;
|
| + case kArrayPop: {
|
| + if (receiver_map.is_null()) return false;
|
| + if (receiver_map->instance_type() != JS_ARRAY_TYPE) return false;
|
| + ElementsKind elements_kind = receiver_map->elements_kind();
|
| + if (!IsFastElementsKind(elements_kind)) return false;
|
| +
|
| + Drop(expr->arguments()->length());
|
| + HValue* result;
|
| + HValue* reduced_length;
|
| + HValue* receiver = Pop();
|
| +
|
| + HValue* checked_object = AddCheckMap(receiver, receiver_map);
|
| + HValue* length = Add<HLoadNamedField>(
|
| + checked_object, static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForArrayLength(elements_kind));
|
| +
|
| + Drop(1); // Function.
|
| +
|
| + { NoObservableSideEffectsScope scope(this);
|
| + IfBuilder length_checker(this);
|
| +
|
| + HValue* bounds_check = length_checker.If<HCompareNumericAndBranch>(
|
| + length, graph()->GetConstant0(), Token::EQ);
|
| + length_checker.Then();
|
| +
|
| + if (!ast_context()->IsEffect()) Push(graph()->GetConstantUndefined());
|
| +
|
| + length_checker.Else();
|
| + HValue* elements = AddLoadElements(checked_object);
|
| + // Ensure that we aren't popping from a copy-on-write array.
|
| + if (IsFastSmiOrObjectElementsKind(elements_kind)) {
|
| + elements = BuildCopyElementsOnWrite(checked_object, elements,
|
| + elements_kind, length);
|
| + }
|
| + reduced_length = AddUncasted<HSub>(length, graph()->GetConstant1());
|
| + result = AddElementAccess(elements, reduced_length, NULL,
|
| + bounds_check, elements_kind, LOAD);
|
| + Factory* factory = isolate()->factory();
|
| + double nan_double = FixedDoubleArray::hole_nan_as_double();
|
| + HValue* hole = IsFastSmiOrObjectElementsKind(elements_kind)
|
| + ? Add<HConstant>(factory->the_hole_value())
|
| + : Add<HConstant>(nan_double);
|
| + if (IsFastSmiOrObjectElementsKind(elements_kind)) {
|
| + elements_kind = FAST_HOLEY_ELEMENTS;
|
| + }
|
| + AddElementAccess(
|
| + elements, reduced_length, hole, bounds_check, elements_kind, STORE);
|
| + Add<HStoreNamedField>(
|
| + checked_object, HObjectAccess::ForArrayLength(elements_kind),
|
| + reduced_length, STORE_TO_INITIALIZED_ENTRY);
|
| +
|
| + if (!ast_context()->IsEffect()) Push(result);
|
| +
|
| + length_checker.End();
|
| + }
|
| + result = ast_context()->IsEffect() ? graph()->GetConstant0() : Top();
|
| + Add<HSimulate>(expr->id(), REMOVABLE_SIMULATE);
|
| + if (!ast_context()->IsEffect()) Drop(1);
|
| +
|
| + ast_context()->ReturnValue(result);
|
| + return true;
|
| + }
|
| + case kArrayPush: {
|
| + if (receiver_map.is_null()) return false;
|
| + if (receiver_map->instance_type() != JS_ARRAY_TYPE) return false;
|
| + ElementsKind elements_kind = receiver_map->elements_kind();
|
| + if (!IsFastElementsKind(elements_kind)) return false;
|
| +
|
| + HValue* op_vals[] = {
|
| + context(),
|
| + // Receiver.
|
| + environment()->ExpressionStackAt(expr->arguments()->length())
|
| + };
|
| +
|
| + const int argc = expr->arguments()->length();
|
| + // Includes receiver.
|
| + PushArgumentsFromEnvironment(argc + 1);
|
| +
|
| + CallInterfaceDescriptor* descriptor =
|
| + isolate()->call_descriptor(Isolate::CallHandler);
|
| +
|
| + ArrayPushStub stub(receiver_map->elements_kind(), argc);
|
| + Handle<Code> code = stub.GetCode(isolate());
|
| + HConstant* code_value = Add<HConstant>(code);
|
| +
|
| + ASSERT((sizeof(op_vals) / kPointerSize) ==
|
| + descriptor->environment_length());
|
| +
|
| + HInstruction* call = New<HCallWithDescriptor>(
|
| + code_value, argc + 1, descriptor,
|
| + Vector<HValue*>(op_vals, descriptor->environment_length()));
|
| + Drop(1); // Drop function.
|
| + ast_context()->ReturnInstruction(call, expr->id());
|
| + return true;
|
| + }
|
| default:
|
| // Not yet supported for inlining.
|
| break;
|
| @@ -7622,12 +7700,188 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
|
| }
|
|
|
|
|
| +bool HOptimizedGraphBuilder::TryInlineApiFunctionCall(Call* expr,
|
| + HValue* receiver) {
|
| + Handle<JSFunction> function = expr->target();
|
| + int argc = expr->arguments()->length();
|
| + SmallMapList receiver_maps;
|
| + return TryInlineApiCall(function,
|
| + receiver,
|
| + &receiver_maps,
|
| + argc,
|
| + expr->id(),
|
| + kCallApiFunction);
|
| +}
|
| +
|
| +
|
| +bool HOptimizedGraphBuilder::TryInlineApiMethodCall(
|
| + Call* expr,
|
| + HValue* receiver,
|
| + SmallMapList* receiver_maps) {
|
| + Handle<JSFunction> function = expr->target();
|
| + int argc = expr->arguments()->length();
|
| + return TryInlineApiCall(function,
|
| + receiver,
|
| + receiver_maps,
|
| + argc,
|
| + expr->id(),
|
| + kCallApiMethod);
|
| +}
|
| +
|
| +
|
| +bool HOptimizedGraphBuilder::TryInlineApiGetter(Handle<JSFunction> function,
|
| + Handle<Map> receiver_map,
|
| + BailoutId ast_id) {
|
| + SmallMapList receiver_maps(1, zone());
|
| + receiver_maps.Add(receiver_map, zone());
|
| + return TryInlineApiCall(function,
|
| + NULL, // Receiver is on expression stack.
|
| + &receiver_maps,
|
| + 0,
|
| + ast_id,
|
| + kCallApiGetter);
|
| +}
|
| +
|
| +
|
| +bool HOptimizedGraphBuilder::TryInlineApiSetter(Handle<JSFunction> function,
|
| + Handle<Map> receiver_map,
|
| + BailoutId ast_id) {
|
| + SmallMapList receiver_maps(1, zone());
|
| + receiver_maps.Add(receiver_map, zone());
|
| + return TryInlineApiCall(function,
|
| + NULL, // Receiver is on expression stack.
|
| + &receiver_maps,
|
| + 1,
|
| + ast_id,
|
| + kCallApiSetter);
|
| +}
|
| +
|
| +
|
| +bool HOptimizedGraphBuilder::TryInlineApiCall(Handle<JSFunction> function,
|
| + HValue* receiver,
|
| + SmallMapList* receiver_maps,
|
| + int argc,
|
| + BailoutId ast_id,
|
| + ApiCallType call_type) {
|
| + CallOptimization optimization(function);
|
| + if (!optimization.is_simple_api_call()) return false;
|
| + Handle<Map> holder_map;
|
| + if (call_type == kCallApiFunction) {
|
| + // Cannot embed a direct reference to the global proxy map
|
| + // as it maybe dropped on deserialization.
|
| + CHECK(!Serializer::enabled());
|
| + ASSERT_EQ(0, receiver_maps->length());
|
| + receiver_maps->Add(handle(
|
| + function->context()->global_object()->global_receiver()->map()),
|
| + zone());
|
| + }
|
| + CallOptimization::HolderLookup holder_lookup =
|
| + CallOptimization::kHolderNotFound;
|
| + Handle<JSObject> api_holder = optimization.LookupHolderOfExpectedType(
|
| + receiver_maps->first(), &holder_lookup);
|
| + if (holder_lookup == CallOptimization::kHolderNotFound) return false;
|
| +
|
| + if (FLAG_trace_inlining) {
|
| + PrintF("Inlining api function ");
|
| + function->ShortPrint();
|
| + PrintF("\n");
|
| + }
|
| +
|
| + bool drop_extra = false;
|
| + bool is_store = false;
|
| + switch (call_type) {
|
| + case kCallApiFunction:
|
| + case kCallApiMethod:
|
| + // Need to check that none of the receiver maps could have changed.
|
| + Add<HCheckMaps>(receiver, receiver_maps);
|
| + // Need to ensure the chain between receiver and api_holder is intact.
|
| + if (holder_lookup == CallOptimization::kHolderFound) {
|
| + AddCheckPrototypeMaps(api_holder, receiver_maps->first());
|
| + } else {
|
| + ASSERT_EQ(holder_lookup, CallOptimization::kHolderIsReceiver);
|
| + }
|
| + // Includes receiver.
|
| + PushArgumentsFromEnvironment(argc + 1);
|
| + // Drop function after call.
|
| + drop_extra = true;
|
| + break;
|
| + case kCallApiGetter:
|
| + // Receiver and prototype chain cannot have changed.
|
| + ASSERT_EQ(0, argc);
|
| + ASSERT_EQ(NULL, receiver);
|
| + // Receiver is on expression stack.
|
| + receiver = Pop();
|
| + Add<HPushArgument>(receiver);
|
| + break;
|
| + case kCallApiSetter:
|
| + {
|
| + is_store = true;
|
| + // Receiver and prototype chain cannot have changed.
|
| + ASSERT_EQ(1, argc);
|
| + ASSERT_EQ(NULL, receiver);
|
| + // Receiver and value are on expression stack.
|
| + HValue* value = Pop();
|
| + receiver = Pop();
|
| + Add<HPushArgument>(receiver);
|
| + Add<HPushArgument>(value);
|
| + break;
|
| + }
|
| + }
|
| +
|
| + HValue* holder = NULL;
|
| + switch (holder_lookup) {
|
| + case CallOptimization::kHolderFound:
|
| + holder = Add<HConstant>(api_holder);
|
| + break;
|
| + case CallOptimization::kHolderIsReceiver:
|
| + holder = receiver;
|
| + break;
|
| + case CallOptimization::kHolderNotFound:
|
| + UNREACHABLE();
|
| + break;
|
| + }
|
| + Handle<CallHandlerInfo> api_call_info = optimization.api_call_info();
|
| + Handle<Object> call_data_obj(api_call_info->data(), isolate());
|
| + bool call_data_is_undefined = call_data_obj->IsUndefined();
|
| + HValue* call_data = Add<HConstant>(call_data_obj);
|
| + ApiFunction fun(v8::ToCData<Address>(api_call_info->callback()));
|
| + ExternalReference ref = ExternalReference(&fun,
|
| + ExternalReference::DIRECT_API_CALL,
|
| + isolate());
|
| + HValue* api_function_address = Add<HConstant>(ExternalReference(ref));
|
| +
|
| + HValue* op_vals[] = {
|
| + Add<HConstant>(function),
|
| + call_data,
|
| + holder,
|
| + api_function_address,
|
| + context()
|
| + };
|
| +
|
| + CallInterfaceDescriptor* descriptor =
|
| + isolate()->call_descriptor(Isolate::ApiFunctionCall);
|
| +
|
| + CallApiFunctionStub stub(is_store, call_data_is_undefined, argc);
|
| + Handle<Code> code = stub.GetCode(isolate());
|
| + HConstant* code_value = Add<HConstant>(code);
|
| +
|
| + ASSERT((sizeof(op_vals) / kPointerSize) ==
|
| + descriptor->environment_length());
|
| +
|
| + HInstruction* call = New<HCallWithDescriptor>(
|
| + code_value, argc + 1, descriptor,
|
| + Vector<HValue*>(op_vals, descriptor->environment_length()));
|
| +
|
| + if (drop_extra) Drop(1); // Drop function.
|
| + ast_context()->ReturnInstruction(call, ast_id);
|
| + return true;
|
| +}
|
| +
|
| +
|
| bool HOptimizedGraphBuilder::TryCallApply(Call* expr) {
|
| - Expression* callee = expr->expression();
|
| - Property* prop = callee->AsProperty();
|
| - ASSERT(prop != NULL);
|
| + ASSERT(expr->expression()->IsProperty());
|
|
|
| - if (!expr->IsMonomorphic() || expr->check_type() != RECEIVER_MAP_CHECK) {
|
| + if (!expr->IsMonomorphic()) {
|
| return false;
|
| }
|
| Handle<Map> function_map = expr->GetReceiverTypes()->first();
|
| @@ -7648,15 +7902,10 @@ bool HOptimizedGraphBuilder::TryCallApply(Call* expr) {
|
| if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
|
|
|
| // Found pattern f.apply(receiver, arguments).
|
| - CHECK_ALIVE_OR_RETURN(VisitForValue(prop->obj()), true);
|
| - HValue* function = Top();
|
| -
|
| - AddCheckConstantFunction(expr->holder(), function, function_map);
|
| -
|
| CHECK_ALIVE_OR_RETURN(VisitForValue(args->at(0)), true);
|
| - HValue* receiver = Pop();
|
| -
|
| - Drop(1); // Pop the function.
|
| + HValue* receiver = Pop(); // receiver
|
| + HValue* function = Pop(); // f
|
| + Drop(1); // apply
|
|
|
| if (function_state()->outer() == NULL) {
|
| HInstruction* elements = Add<HArgumentsElements>(false);
|
| @@ -7676,6 +7925,7 @@ bool HOptimizedGraphBuilder::TryCallApply(Call* expr) {
|
| HArgumentsObject* args = function_state()->entry()->arguments_object();
|
| const ZoneList<HValue*>* arguments_values = args->arguments_values();
|
| int arguments_count = arguments_values->length();
|
| + Push(function);
|
| Push(BuildWrapReceiver(receiver, function));
|
| for (int i = 1; i < arguments_count; i++) {
|
| Push(arguments_values->at(i));
|
| @@ -7690,16 +7940,10 @@ bool HOptimizedGraphBuilder::TryCallApply(Call* expr) {
|
| if (TryInlineApply(known_function, expr, args_count)) return true;
|
| }
|
|
|
| - Drop(arguments_count - 1);
|
| - Push(Add<HPushArgument>(Pop()));
|
| - for (int i = 1; i < arguments_count; i++) {
|
| - Push(Add<HPushArgument>(arguments_values->at(i)));
|
| - }
|
| -
|
| - HInvokeFunction* call = New<HInvokeFunction>(function,
|
| - known_function,
|
| - arguments_count);
|
| - Drop(arguments_count);
|
| + PushArgumentsFromEnvironment(arguments_count);
|
| + HInvokeFunction* call = New<HInvokeFunction>(
|
| + function, known_function, arguments_count);
|
| + Drop(1); // Function.
|
| ast_context()->ReturnInstruction(call, expr->id());
|
| return true;
|
| }
|
| @@ -7710,16 +7954,12 @@ HValue* HOptimizedGraphBuilder::ImplicitReceiverFor(HValue* function,
|
| Handle<JSFunction> target) {
|
| SharedFunctionInfo* shared = target->shared();
|
| if (shared->is_classic_mode() && !shared->native()) {
|
| - HValue* context = Add<HLoadNamedField>(
|
| - function,
|
| - HObjectAccess::ForJSObjectOffset(JSFunction::kContextOffset));
|
| - HValue* global_object = Add<HLoadNamedField>(
|
| - context,
|
| - HObjectAccess::ForContextSlot(Context::GLOBAL_OBJECT_INDEX));
|
| - return Add<HLoadNamedField>(
|
| - global_object,
|
| - HObjectAccess::ForJSObjectOffset(
|
| - GlobalObject::kGlobalReceiverOffset));
|
| + // Cannot embed a direct reference to the global proxy
|
| + // as is it dropped on deserialization.
|
| + CHECK(!Serializer::enabled());
|
| + Handle<JSObject> global_receiver(
|
| + target->context()->global_object()->global_receiver());
|
| + return Add<HConstant>(global_receiver);
|
| }
|
| return graph()->GetConstantUndefined();
|
| }
|
| @@ -7735,87 +7975,80 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
|
|
|
| Property* prop = callee->AsProperty();
|
| if (prop != NULL) {
|
| - if (!prop->key()->IsPropertyName()) {
|
| - // Keyed function call.
|
| - CHECK_ALIVE(VisitForValue(prop->obj()));
|
| - CHECK_ALIVE(VisitForValue(prop->key()));
|
| + CHECK_ALIVE(VisitForValue(prop->obj()));
|
| + HValue* receiver = Top();
|
|
|
| - // Push receiver and key like the non-optimized code generator expects it.
|
| - HValue* key = Pop();
|
| - HValue* receiver = Pop();
|
| - Push(key);
|
| - Push(Add<HPushArgument>(receiver));
|
| - CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
| + SmallMapList* types;
|
| + ComputeReceiverTypes(expr, receiver, &types, zone());
|
|
|
| - if (expr->IsMonomorphic()) {
|
| - BuildCheckHeapObject(receiver);
|
| - ElementsKind kind = expr->KeyedArrayCallIsHoley()
|
| - ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS;
|
| + if (prop->key()->IsPropertyName() && types->length() > 0) {
|
| + Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
|
| + PropertyAccessInfo info(this, LOAD, ToType(types->first()), name);
|
| + if (!info.CanAccessAsMonomorphic(types)) {
|
| + HandlePolymorphicCallNamed(expr, receiver, types, name);
|
| + return;
|
| + }
|
| + }
|
|
|
| - Handle<Map> map(isolate()->get_initial_js_array_map(kind));
|
| + HValue* key = NULL;
|
| + if (!prop->key()->IsPropertyName()) {
|
| + CHECK_ALIVE(VisitForValue(prop->key()));
|
| + key = Pop();
|
| + }
|
|
|
| - HValue* function = BuildMonomorphicElementAccess(
|
| - receiver, key, NULL, NULL, map, false, STANDARD_STORE);
|
| + CHECK_ALIVE(PushLoad(prop, receiver, key));
|
| + HValue* function = Pop();
|
|
|
| - call = New<HCallFunction>(function, argument_count);
|
| - } else {
|
| - call = NewCallKeyed(key, argument_count);
|
| - }
|
| - Drop(argument_count + 1); // 1 is the key.
|
| - return ast_context()->ReturnInstruction(call, expr->id());
|
| - }
|
| + if (FLAG_hydrogen_track_positions) SetSourcePosition(expr->position());
|
|
|
| - // Named function call.
|
| - if (TryCallApply(expr)) return;
|
| + // Push the function under the receiver.
|
| + environment()->SetExpressionStackAt(0, function);
|
|
|
| - CHECK_ALIVE(VisitForValue(prop->obj()));
|
| - CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| + Push(receiver);
|
|
|
| - Handle<String> name = prop->key()->AsLiteral()->AsPropertyName();
|
| - HValue* receiver =
|
| - environment()->ExpressionStackAt(expr->arguments()->length());
|
| + if (function->IsConstant() &&
|
| + HConstant::cast(function)->handle(isolate())->IsJSFunction()) {
|
| + Handle<JSFunction> known_function = Handle<JSFunction>::cast(
|
| + HConstant::cast(function)->handle(isolate()));
|
| + expr->set_target(known_function);
|
|
|
| - SmallMapList* types;
|
| - bool was_monomorphic = expr->IsMonomorphic();
|
| - bool monomorphic = ComputeReceiverTypes(expr, receiver, &types);
|
| - if (!was_monomorphic && monomorphic) {
|
| - monomorphic = expr->ComputeTarget(types->first(), name);
|
| - }
|
| + if (TryCallApply(expr)) return;
|
| + CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
|
|
| - if (monomorphic) {
|
| - Handle<Map> map = types->first();
|
| - if (TryInlineBuiltinMethodCall(expr, receiver, map, expr->check_type())) {
|
| + Handle<Map> map = types->length() == 1 ? types->first() : Handle<Map>();
|
| + if (TryInlineBuiltinMethodCall(expr, receiver, map)) {
|
| if (FLAG_trace_inlining) {
|
| PrintF("Inlining builtin ");
|
| - expr->target()->ShortPrint();
|
| + known_function->ShortPrint();
|
| PrintF("\n");
|
| }
|
| return;
|
| }
|
| -
|
| - 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
|
| - // when a primitive receiver check is required.
|
| - call = NewCallNamed(name, argument_count);
|
| - PushArgumentsFromEnvironment(argument_count);
|
| + if (TryInlineApiMethodCall(expr, receiver, types)) return;
|
| +
|
| + // Wrap the receiver if necessary.
|
| + if (NeedsWrappingFor(ToType(types->first()), known_function)) {
|
| + // Since HWrapReceiver currently cannot actually wrap numbers and
|
| + // strings, use the regular CallFunctionStub for method calls to wrap
|
| + // the receiver.
|
| + // TODO(verwaest): Support creation of value wrappers directly in
|
| + // HWrapReceiver.
|
| + call = New<HCallFunction>(
|
| + function, argument_count, WRAP_AND_CALL);
|
| + } else if (TryInlineCall(expr)) {
|
| + return;
|
| } else {
|
| - AddCheckConstantFunction(expr->holder(), receiver, map);
|
| -
|
| - if (TryInlineCall(expr)) return;
|
| - call = BuildCallConstantFunction(expr->target(), argument_count);
|
| - PushArgumentsFromEnvironment(argument_count);
|
| + call = BuildCallConstantFunction(known_function, argument_count);
|
| }
|
| - } else if (types != NULL && types->length() > 1) {
|
| - ASSERT(expr->check_type() == RECEIVER_MAP_CHECK);
|
| - HandlePolymorphicCallNamed(expr, receiver, types, name);
|
| - return;
|
|
|
| } else {
|
| - call = NewCallNamed(name, argument_count);
|
| - PushArgumentsFromEnvironment(argument_count);
|
| + CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| + CallFunctionFlags flags = receiver->type().IsJSObject()
|
| + ? NO_CALL_FUNCTION_FLAGS : CALL_AS_METHOD;
|
| + call = New<HCallFunction>(function, argument_count, flags);
|
| }
|
| + PushArgumentsFromEnvironment(argument_count);
|
| +
|
| } else {
|
| VariableProxy* proxy = expr->expression()->AsVariableProxy();
|
| if (proxy != NULL && proxy->var()->is_possibly_eval(isolate())) {
|
| @@ -7830,30 +8063,27 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
|
| // access check is not enabled we assume that the function will not change
|
| // and generate optimized code for calling the function.
|
| LookupResult lookup(isolate());
|
| - GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, false);
|
| + GlobalPropertyAccess type = LookupGlobalProperty(var, &lookup, LOAD);
|
| if (type == kUseCell &&
|
| !current_info()->global_object()->IsAccessCheckNeeded()) {
|
| Handle<GlobalObject> global(current_info()->global_object());
|
| known_global_function = expr->ComputeGlobalTarget(global, &lookup);
|
| }
|
| + CHECK_ALIVE(VisitForValue(expr->expression()));
|
| + HValue* function = Top();
|
| if (known_global_function) {
|
| - // Push the global object instead of the global receiver because
|
| - // code generated by the full code generator expects it.
|
| - HGlobalObject* global_object = Add<HGlobalObject>();
|
| - Push(global_object);
|
| + Add<HCheckValue>(function, expr->target());
|
|
|
| + // Placeholder for the receiver.
|
| + Push(graph()->GetConstantUndefined());
|
| CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
|
|
| - CHECK_ALIVE(VisitForValue(expr->expression()));
|
| - HValue* function = Pop();
|
| - Add<HCheckValue>(function, expr->target());
|
| -
|
| // Patch the global object on the stack by the expected receiver.
|
| HValue* receiver = ImplicitReceiverFor(function, expr->target());
|
| const int receiver_index = argument_count - 1;
|
| environment()->SetExpressionStackAt(receiver_index, receiver);
|
|
|
| - if (TryInlineBuiltinFunctionCall(expr, false)) { // Nothing to drop.
|
| + if (TryInlineBuiltinFunctionCall(expr)) {
|
| if (FLAG_trace_inlining) {
|
| PrintF("Inlining builtin ");
|
| expr->target()->ShortPrint();
|
| @@ -7861,32 +8091,15 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
|
| }
|
| return;
|
| }
|
| + if (TryInlineApiFunctionCall(expr, receiver)) return;
|
| if (TryInlineCall(expr)) return;
|
|
|
| - if (expr->target().is_identical_to(current_info()->closure())) {
|
| - graph()->MarkRecursive();
|
| - }
|
| -
|
| - if (CallStubCompiler::HasCustomCallGenerator(expr->target())) {
|
| - // We're about to install a contextual IC, which expects the global
|
| - // object as receiver rather than the global proxy.
|
| - HGlobalObject* global_object = Add<HGlobalObject>();
|
| - const int receiver_index = argument_count - 1;
|
| - environment()->SetExpressionStackAt(receiver_index, global_object);
|
| - // When the target has a custom call IC generator, use the IC,
|
| - // because it is likely to generate better code.
|
| - call = NewCallNamed(var->name(), argument_count);
|
| - PushArgumentsFromEnvironment(argument_count);
|
| - } else {
|
| - call = BuildCallConstantFunction(expr->target(), argument_count);
|
| - PushArgumentsFromEnvironment(argument_count);
|
| - }
|
| + PushArgumentsFromEnvironment(argument_count);
|
| + call = BuildCallConstantFunction(expr->target(), argument_count);
|
| } else {
|
| - HGlobalObject* receiver = Add<HGlobalObject>();
|
| - Push(Add<HPushArgument>(receiver));
|
| + Push(Add<HPushArgument>(graph()->GetConstantUndefined()));
|
| CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
| -
|
| - call = NewCallNamed(var->name(), argument_count);
|
| + call = New<HCallFunction>(function, argument_count);
|
| Drop(argument_count);
|
| }
|
|
|
| @@ -7898,12 +8111,14 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
|
|
|
| Add<HCheckValue>(function, expr->target());
|
|
|
| - HValue* receiver = ImplicitReceiverFor(function, expr->target());
|
| - Push(receiver);
|
| -
|
| + Push(graph()->GetConstantUndefined());
|
| CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
|
|
| - if (TryInlineBuiltinFunctionCall(expr, true)) { // Drop the function.
|
| + HValue* receiver = ImplicitReceiverFor(function, expr->target());
|
| + const int receiver_index = argument_count - 1;
|
| + environment()->SetExpressionStackAt(receiver_index, receiver);
|
| +
|
| + if (TryInlineBuiltinFunctionCall(expr)) {
|
| if (FLAG_trace_inlining) {
|
| PrintF("Inlining builtin ");
|
| expr->target()->ShortPrint();
|
| @@ -7911,14 +8126,12 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
|
| }
|
| return;
|
| }
|
| + if (TryInlineApiFunctionCall(expr, receiver)) return;
|
|
|
| - if (TryInlineCall(expr, true)) { // Drop function from environment.
|
| - return;
|
| - } else {
|
| - call = PreProcessCall(New<HInvokeFunction>(function, expr->target(),
|
| - argument_count));
|
| - Drop(1); // The function.
|
| - }
|
| + if (TryInlineCall(expr)) return;
|
| +
|
| + call = PreProcessCall(New<HInvokeFunction>(
|
| + function, expr->target(), argument_count));
|
|
|
| } else {
|
| CHECK_ALIVE(VisitForValue(expr->expression()));
|
| @@ -7926,12 +8139,12 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
|
| HValue* receiver = graph()->GetConstantUndefined();
|
| Push(Add<HPushArgument>(receiver));
|
| CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
| - call = New<HCallFunction>(
|
| - function, argument_count, NORMAL_CONTEXTUAL_CALL);
|
| - Drop(argument_count + 1);
|
| + call = New<HCallFunction>(function, argument_count);
|
| + Drop(argument_count);
|
| }
|
| }
|
|
|
| + Drop(1); // Drop the function.
|
| return ast_context()->ReturnInstruction(call, expr->id());
|
| }
|
|
|
| @@ -7944,8 +8157,8 @@ void HOptimizedGraphBuilder::BuildInlinedCallNewArray(CallNew* expr) {
|
| HValue* constructor = environment()->ExpressionStackAt(argument_count);
|
|
|
| ElementsKind kind = expr->elements_kind();
|
| - Handle<Cell> cell = expr->allocation_info_cell();
|
| - Handle<AllocationSite> site(AllocationSite::cast(cell->value()));
|
| + Handle<AllocationSite> site = expr->allocation_site();
|
| + ASSERT(!site.is_null());
|
|
|
| // Register on the site for deoptimization if the transition feedback changes.
|
| AllocationSite::AddDependentCompilationInfo(
|
| @@ -8012,15 +8225,16 @@ static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
|
|
|
|
|
| bool HOptimizedGraphBuilder::IsCallNewArrayInlineable(CallNew* expr) {
|
| - bool inline_ok = false;
|
| Handle<JSFunction> caller = current_info()->closure();
|
| - Handle<JSFunction> target(isolate()->global_context()->array_function(),
|
| + Handle<JSFunction> target(isolate()->native_context()->array_function(),
|
| isolate());
|
| int argument_count = expr->arguments()->length();
|
| // We should have the function plus array arguments on the environment stack.
|
| ASSERT(environment()->length() >= (argument_count + 1));
|
| - Handle<Cell> cell = expr->allocation_info_cell();
|
| - AllocationSite* site = AllocationSite::cast(cell->value());
|
| + Handle<AllocationSite> site = expr->allocation_site();
|
| + ASSERT(!site.is_null());
|
| +
|
| + bool inline_ok = false;
|
| if (site->CanInlineCall()) {
|
| // We also want to avoid inlining in certain 1 argument scenarios.
|
| if (argument_count == 1) {
|
| @@ -8059,7 +8273,7 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| - if (!FLAG_emit_opt_code_positions) SetSourcePosition(expr->position());
|
| + if (!FLAG_hydrogen_track_positions) SetSourcePosition(expr->position());
|
| int argument_count = expr->arguments()->length() + 1; // Plus constructor.
|
| Factory* factory = isolate()->factory();
|
|
|
| @@ -8090,9 +8304,8 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
|
| // Allocate an instance of the implicit receiver object.
|
| HValue* size_in_bytes = Add<HConstant>(instance_size);
|
| PretenureFlag pretenure_flag =
|
| - (FLAG_pretenuring_call_new &&
|
| - isolate()->heap()->GetPretenureMode() == TENURED)
|
| - ? TENURED : NOT_TENURED;
|
| + (FLAG_pretenuring_call_new && !FLAG_allocation_site_pretenuring) ?
|
| + isolate()->heap()->GetPretenureMode() : NOT_TENURED;
|
| HAllocate* receiver =
|
| Add<HAllocate>(size_in_bytes, HType::JSObject(), pretenure_flag,
|
| JS_OBJECT_TYPE);
|
| @@ -8101,28 +8314,32 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
|
| // Load the initial map from the constructor.
|
| HValue* constructor_value = Add<HConstant>(constructor);
|
| HValue* initial_map_value =
|
| - Add<HLoadNamedField>(constructor_value, HObjectAccess::ForJSObjectOffset(
|
| - JSFunction::kPrototypeOrInitialMapOffset));
|
| + Add<HLoadNamedField>(constructor_value, static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForMapAndOffset(
|
| + handle(constructor->map()),
|
| + JSFunction::kPrototypeOrInitialMapOffset));
|
|
|
| // Initialize map and fields of the newly allocated object.
|
| { NoObservableSideEffectsScope no_effects(this);
|
| ASSERT(initial_map->instance_type() == JS_OBJECT_TYPE);
|
| Add<HStoreNamedField>(receiver,
|
| - HObjectAccess::ForJSObjectOffset(JSObject::kMapOffset),
|
| + HObjectAccess::ForMapAndOffset(initial_map, JSObject::kMapOffset),
|
| initial_map_value);
|
| HValue* empty_fixed_array = Add<HConstant>(factory->empty_fixed_array());
|
| Add<HStoreNamedField>(receiver,
|
| - HObjectAccess::ForJSObjectOffset(JSObject::kPropertiesOffset),
|
| + HObjectAccess::ForMapAndOffset(initial_map,
|
| + JSObject::kPropertiesOffset),
|
| empty_fixed_array);
|
| Add<HStoreNamedField>(receiver,
|
| - HObjectAccess::ForJSObjectOffset(JSObject::kElementsOffset),
|
| + HObjectAccess::ForMapAndOffset(initial_map,
|
| + JSObject::kElementsOffset),
|
| empty_fixed_array);
|
| if (initial_map->inobject_properties() != 0) {
|
| HConstant* undefined = graph()->GetConstantUndefined();
|
| for (int i = 0; i < initial_map->inobject_properties(); i++) {
|
| - int property_offset = JSObject::kHeaderSize + i * kPointerSize;
|
| + int property_offset = initial_map->GetInObjectPropertyOffset(i);
|
| Add<HStoreNamedField>(receiver,
|
| - HObjectAccess::ForJSObjectOffset(property_offset),
|
| + HObjectAccess::ForMapAndOffset(initial_map, property_offset),
|
| undefined);
|
| }
|
| }
|
| @@ -8157,9 +8374,8 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
|
| // The constructor function is both an operand to the instruction and an
|
| // argument to the construct call.
|
| Handle<JSFunction> array_function(
|
| - isolate()->global_context()->array_function(), isolate());
|
| + isolate()->native_context()->array_function(), isolate());
|
| bool use_call_new_array = expr->target().is_identical_to(array_function);
|
| - Handle<Cell> cell = expr->allocation_info_cell();
|
| if (use_call_new_array && IsCallNewArrayInlineable(expr)) {
|
| // Verify we are still calling the array function for our native context.
|
| Add<HCheckValue>(function, array_function);
|
| @@ -8170,7 +8386,7 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) {
|
| HBinaryCall* call;
|
| if (use_call_new_array) {
|
| Add<HCheckValue>(function, array_function);
|
| - call = New<HCallNewArray>(function, argument_count, cell,
|
| + call = New<HCallNewArray>(function, argument_count,
|
| expr->elements_kind());
|
| } else {
|
| call = New<HCallNew>(function, argument_count);
|
| @@ -8208,8 +8424,8 @@ void HGraphBuilder::BuildArrayBufferViewInitialization(
|
| offset < ViewClass::kSizeWithInternalFields;
|
| offset += kPointerSize) {
|
| Add<HStoreNamedField>(obj,
|
| - HObjectAccess::ForJSObjectOffset(offset),
|
| - Add<HConstant>(static_cast<int32_t>(0)));
|
| + HObjectAccess::ForObservableJSObjectOffset(offset),
|
| + graph()->GetConstant0());
|
| }
|
|
|
| Add<HStoreNamedField>(
|
| @@ -8228,8 +8444,10 @@ void HGraphBuilder::BuildArrayBufferViewInitialization(
|
| HObjectAccess::ForJSArrayBufferWeakFirstView();
|
| Add<HStoreNamedField>(obj,
|
| HObjectAccess::ForJSArrayBufferViewWeakNext(),
|
| - Add<HLoadNamedField>(buffer, weak_first_view_access));
|
| - Add<HStoreNamedField>(buffer, weak_first_view_access, obj);
|
| + Add<HLoadNamedField>(buffer, static_cast<HValue*>(NULL),
|
| + weak_first_view_access));
|
| + Add<HStoreNamedField>(
|
| + buffer, weak_first_view_access, obj);
|
| }
|
|
|
|
|
| @@ -8310,7 +8528,7 @@ void HOptimizedGraphBuilder::VisitTypedArrayInitialize(
|
| BuildArrayBufferViewInitialization<JSTypedArray>(
|
| obj, buffer, byte_offset, byte_length);
|
|
|
| - ExternalArrayType array_type = kExternalByteArray; // Bogus initialization.
|
| + ExternalArrayType array_type = kExternalInt8Array; // Bogus initialization.
|
| size_t element_size = 1; // Bogus initialization.
|
| Runtime::ArrayIdToTypeAndSize(array_id, &array_type, &element_size);
|
|
|
| @@ -8321,21 +8539,21 @@ void HOptimizedGraphBuilder::VisitTypedArrayInitialize(
|
| HObjectAccess::ForJSTypedArrayLength(),
|
| length);
|
|
|
| + Handle<Map> external_array_map(
|
| + isolate()->heap()->MapForExternalArrayType(array_type));
|
| +
|
| HValue* elements =
|
| Add<HAllocate>(
|
| Add<HConstant>(ExternalArray::kAlignedSize),
|
| HType::JSArray(),
|
| NOT_TENURED,
|
| - static_cast<InstanceType>(FIRST_EXTERNAL_ARRAY_TYPE + array_type));
|
| + external_array_map->instance_type());
|
|
|
| - Handle<Map> external_array_map(
|
| - isolate()->heap()->MapForExternalArrayType(array_type));
|
| - Add<HStoreNamedField>(elements,
|
| - HObjectAccess::ForMap(),
|
| - Add<HConstant>(external_array_map));
|
| + AddStoreMapConstant(elements, external_array_map);
|
|
|
| HValue* backing_store = Add<HLoadNamedField>(
|
| - buffer, HObjectAccess::ForJSArrayBufferBackingStore());
|
| + buffer, static_cast<HValue*>(NULL),
|
| + HObjectAccess::ForJSArrayBufferBackingStore());
|
|
|
| HValue* typed_array_start;
|
| if (is_zero_byte_offset) {
|
| @@ -8543,8 +8761,7 @@ HInstruction* HOptimizedGraphBuilder::BuildIncrement(
|
| bool returns_original_input,
|
| CountOperation* expr) {
|
| // The input to the count operation is on top of the expression stack.
|
| - Handle<Type> info = expr->type();
|
| - Representation rep = Representation::FromType(info);
|
| + Representation rep = Representation::FromType(expr->type());
|
| if (rep.IsNone() || rep.IsTagged()) {
|
| rep = Representation::Smi();
|
| }
|
| @@ -8599,7 +8816,7 @@ void HOptimizedGraphBuilder::VisitCountOperation(CountOperation* expr) {
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| - if (!FLAG_emit_opt_code_positions) SetSourcePosition(expr->position());
|
| + if (!FLAG_hydrogen_track_positions) SetSourcePosition(expr->position());
|
| Expression* target = expr->expression();
|
| VariableProxy* proxy = target->AsVariableProxy();
|
| Property* prop = target->AsProperty();
|
| @@ -8795,7 +9012,7 @@ bool CanBeZero(HValue* right) {
|
|
|
|
|
| HValue* HGraphBuilder::EnforceNumberType(HValue* number,
|
| - Handle<Type> expected) {
|
| + Type* expected) {
|
| if (expected->Is(Type::Smi())) {
|
| return AddUncasted<HForceRepresentation>(number, Representation::Smi());
|
| }
|
| @@ -8807,12 +9024,12 @@ HValue* HGraphBuilder::EnforceNumberType(HValue* number,
|
| }
|
|
|
|
|
| -HValue* HGraphBuilder::TruncateToNumber(HValue* value, Handle<Type>* expected) {
|
| +HValue* HGraphBuilder::TruncateToNumber(HValue* value, Type** expected) {
|
| if (value->IsConstant()) {
|
| HConstant* constant = HConstant::cast(value);
|
| Maybe<HConstant*> number = constant->CopyToTruncatedNumber(zone());
|
| if (number.has_value) {
|
| - *expected = Type::Number(isolate());
|
| + *expected = Type::Number(zone());
|
| return AddInstruction(number.value);
|
| }
|
| }
|
| @@ -8822,25 +9039,24 @@ HValue* HGraphBuilder::TruncateToNumber(HValue* value, Handle<Type>* expected) {
|
| // pushes with a NoObservableSideEffectsScope.
|
| NoObservableSideEffectsScope no_effects(this);
|
|
|
| - Handle<Type> expected_type = *expected;
|
| + Type* expected_type = *expected;
|
|
|
| // Separate the number type from the rest.
|
| - Handle<Type> expected_obj = Type::Intersect(
|
| - expected_type, Type::NonNumber(isolate()), isolate());
|
| - Handle<Type> expected_number = Type::Intersect(
|
| - expected_type, Type::Number(isolate()), isolate());
|
| + Type* expected_obj =
|
| + Type::Intersect(expected_type, Type::NonNumber(zone()), zone());
|
| + Type* expected_number =
|
| + Type::Intersect(expected_type, Type::Number(zone()), zone());
|
|
|
| // We expect to get a number.
|
| // (We need to check first, since Type::None->Is(Type::Any()) == true.
|
| if (expected_obj->Is(Type::None())) {
|
| - ASSERT(!expected_number->Is(Type::None()));
|
| + ASSERT(!expected_number->Is(Type::None(zone())));
|
| return value;
|
| }
|
|
|
| - if (expected_obj->Is(Type::Undefined())) {
|
| + if (expected_obj->Is(Type::Undefined(zone()))) {
|
| // This is already done by HChange.
|
| - *expected = Type::Union(
|
| - expected_number, Type::Double(isolate()), isolate());
|
| + *expected = Type::Union(expected_number, Type::Double(zone()), zone());
|
| return value;
|
| }
|
|
|
| @@ -8851,19 +9067,23 @@ HValue* HGraphBuilder::TruncateToNumber(HValue* value, Handle<Type>* expected) {
|
| HValue* HOptimizedGraphBuilder::BuildBinaryOperation(
|
| BinaryOperation* expr,
|
| HValue* left,
|
| - HValue* right) {
|
| - Handle<Type> left_type = expr->left()->bounds().lower;
|
| - Handle<Type> right_type = expr->right()->bounds().lower;
|
| - Handle<Type> result_type = expr->bounds().lower;
|
| + HValue* right,
|
| + PushBeforeSimulateBehavior push_sim_result) {
|
| + Type* left_type = expr->left()->bounds().lower;
|
| + Type* right_type = expr->right()->bounds().lower;
|
| + Type* result_type = expr->bounds().lower;
|
| Maybe<int> fixed_right_arg = expr->fixed_right_arg();
|
| Handle<AllocationSite> allocation_site = expr->allocation_site();
|
|
|
| + PretenureFlag pretenure_flag = !FLAG_allocation_site_pretenuring ?
|
| + isolate()->heap()->GetPretenureMode() : NOT_TENURED;
|
| +
|
| HAllocationMode allocation_mode =
|
| FLAG_allocation_site_pretenuring
|
| ? (allocation_site.is_null()
|
| ? HAllocationMode(NOT_TENURED)
|
| : HAllocationMode(allocation_site))
|
| - : HAllocationMode(isolate()->heap()->GetPretenureMode());
|
| + : HAllocationMode(pretenure_flag);
|
|
|
| HValue* result = HGraphBuilder::BuildBinaryOperation(
|
| expr->op(), left, right, left_type, right_type, result_type,
|
| @@ -8872,9 +9092,13 @@ HValue* HOptimizedGraphBuilder::BuildBinaryOperation(
|
| // after phis, which are the result of BuildBinaryOperation when we
|
| // inlined some complex subgraph.
|
| if (result->HasObservableSideEffects() || result->IsPhi()) {
|
| - Push(result);
|
| - Add<HSimulate>(expr->id(), REMOVABLE_SIMULATE);
|
| - Drop(1);
|
| + if (push_sim_result == PUSH_BEFORE_SIMULATE) {
|
| + Push(result);
|
| + Add<HSimulate>(expr->id(), REMOVABLE_SIMULATE);
|
| + Drop(1);
|
| + } else {
|
| + Add<HSimulate>(expr->id(), REMOVABLE_SIMULATE);
|
| + }
|
| }
|
| return result;
|
| }
|
| @@ -8884,9 +9108,9 @@ HValue* HGraphBuilder::BuildBinaryOperation(
|
| Token::Value op,
|
| HValue* left,
|
| HValue* right,
|
| - Handle<Type> left_type,
|
| - Handle<Type> right_type,
|
| - Handle<Type> result_type,
|
| + Type* left_type,
|
| + Type* right_type,
|
| + Type* result_type,
|
| Maybe<int> fixed_right_arg,
|
| HAllocationMode allocation_mode) {
|
|
|
| @@ -8902,7 +9126,7 @@ HValue* HGraphBuilder::BuildBinaryOperation(
|
| Deoptimizer::SOFT);
|
| // TODO(rossberg): we should be able to get rid of non-continuous
|
| // defaults.
|
| - left_type = Type::Any(isolate());
|
| + left_type = Type::Any(zone());
|
| } else {
|
| if (!maybe_string_add) left = TruncateToNumber(left, &left_type);
|
| left_rep = Representation::FromType(left_type);
|
| @@ -8911,7 +9135,7 @@ HValue* HGraphBuilder::BuildBinaryOperation(
|
| if (right_type->Is(Type::None())) {
|
| Add<HDeoptimize>("Insufficient type feedback for RHS of binary operation",
|
| Deoptimizer::SOFT);
|
| - right_type = Type::Any(isolate());
|
| + right_type = Type::Any(zone());
|
| } else {
|
| if (!maybe_string_add) right = TruncateToNumber(right, &right_type);
|
| right_rep = Representation::FromType(right_type);
|
| @@ -8954,11 +9178,16 @@ HValue* HGraphBuilder::BuildBinaryOperation(
|
| return AddUncasted<HInvokeFunction>(function, 2);
|
| }
|
|
|
| - // Inline the string addition into the stub when creating allocation
|
| - // mementos to gather allocation site feedback.
|
| - if (graph()->info()->IsStub() &&
|
| - allocation_mode.CreateAllocationMementos()) {
|
| - return BuildStringAdd(left, right, allocation_mode);
|
| + // Fast path for empty constant strings.
|
| + if (left->IsConstant() &&
|
| + HConstant::cast(left)->HasStringValue() &&
|
| + HConstant::cast(left)->StringValue()->length() == 0) {
|
| + return right;
|
| + }
|
| + if (right->IsConstant() &&
|
| + HConstant::cast(right)->HasStringValue() &&
|
| + HConstant::cast(right)->StringValue()->length() == 0) {
|
| + return left;
|
| }
|
|
|
| // Register the dependent code with the allocation site.
|
| @@ -8969,28 +9198,20 @@ HValue* HGraphBuilder::BuildBinaryOperation(
|
| site, AllocationSite::TENURING, top_info());
|
| }
|
|
|
| - // Inline string addition if we know that we'll create a cons string.
|
| - if (left->IsConstant()) {
|
| - HConstant* c_left = HConstant::cast(left);
|
| - if (c_left->HasStringValue()) {
|
| - int c_left_length = c_left->StringValue()->length();
|
| - if (c_left_length == 0) {
|
| - return right;
|
| - } else if (c_left_length + 1 >= ConsString::kMinLength) {
|
| - return BuildStringAdd(left, right, allocation_mode);
|
| - }
|
| - }
|
| - }
|
| - if (right->IsConstant()) {
|
| - HConstant* c_right = HConstant::cast(right);
|
| - if (c_right->HasStringValue()) {
|
| - int c_right_length = c_right->StringValue()->length();
|
| - if (c_right_length == 0) {
|
| - return left;
|
| - } else if (c_right_length + 1 >= ConsString::kMinLength) {
|
| - return BuildStringAdd(left, right, allocation_mode);
|
| - }
|
| - }
|
| + // Inline the string addition into the stub when creating allocation
|
| + // mementos to gather allocation site feedback, or if we can statically
|
| + // infer that we're going to create a cons string.
|
| + if ((graph()->info()->IsStub() &&
|
| + allocation_mode.CreateAllocationMementos()) ||
|
| + (left->IsConstant() &&
|
| + HConstant::cast(left)->HasStringValue() &&
|
| + HConstant::cast(left)->StringValue()->length() + 1 >=
|
| + ConsString::kMinLength) ||
|
| + (right->IsConstant() &&
|
| + HConstant::cast(right)->HasStringValue() &&
|
| + HConstant::cast(right)->StringValue()->length() + 1 >=
|
| + ConsString::kMinLength)) {
|
| + return BuildStringAdd(left, right, allocation_mode);
|
| }
|
|
|
| // Fallback to using the string add stub.
|
| @@ -9177,11 +9398,14 @@ void HOptimizedGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) {
|
| ASSERT(current_block() != NULL);
|
| HValue* left_value = Top();
|
|
|
| - if (left_value->IsConstant()) {
|
| - HConstant* left_constant = HConstant::cast(left_value);
|
| - if ((is_logical_and && left_constant->BooleanValue()) ||
|
| - (!is_logical_and && !left_constant->BooleanValue())) {
|
| - Drop(1); // left_value.
|
| + // Short-circuit left values that always evaluate to the same boolean value.
|
| + if (expr->left()->ToBooleanIsTrue() || expr->left()->ToBooleanIsFalse()) {
|
| + // l (evals true) && r -> r
|
| + // l (evals true) || r -> l
|
| + // l (evals false) && r -> l
|
| + // l (evals false) || r -> r
|
| + if (is_logical_and == expr->left()->ToBooleanIsTrue()) {
|
| + Drop(1);
|
| CHECK_ALIVE(VisitForValue(expr->right()));
|
| }
|
| return ast_context()->ReturnValue(Pop());
|
| @@ -9254,10 +9478,15 @@ void HOptimizedGraphBuilder::VisitArithmeticExpression(BinaryOperation* expr) {
|
| SetSourcePosition(expr->position());
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
| - HValue* result = BuildBinaryOperation(expr, left, right);
|
| - if (FLAG_emit_opt_code_positions && result->IsBinaryOperation()) {
|
| + HValue* result =
|
| + BuildBinaryOperation(expr, left, right,
|
| + ast_context()->IsEffect() ? NO_PUSH_BEFORE_SIMULATE
|
| + : PUSH_BEFORE_SIMULATE);
|
| + if (FLAG_hydrogen_track_positions && result->IsBinaryOperation()) {
|
| HBinaryOperation::cast(result)->SetOperandPositions(
|
| - zone(), expr->left()->position(), expr->right()->position());
|
| + zone(),
|
| + ScriptPositionToSourcePosition(expr->left()->position()),
|
| + ScriptPositionToSourcePosition(expr->right()->position()));
|
| }
|
| return ast_context()->ReturnValue(result);
|
| }
|
| @@ -9291,7 +9520,7 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
|
|
| - if (!FLAG_emit_opt_code_positions) SetSourcePosition(expr->position());
|
| + if (!FLAG_hydrogen_track_positions) SetSourcePosition(expr->position());
|
|
|
| // Check for a few fast cases. The AST visiting behavior must be in sync
|
| // with the full codegen: We don't push both left and right values onto
|
| @@ -9319,14 +9548,14 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
| return ast_context()->ReturnControl(instr, expr->id());
|
| }
|
|
|
| - Handle<Type> left_type = expr->left()->bounds().lower;
|
| - Handle<Type> right_type = expr->right()->bounds().lower;
|
| - Handle<Type> combined_type = expr->combined_type();
|
| + Type* left_type = expr->left()->bounds().lower;
|
| + Type* right_type = expr->right()->bounds().lower;
|
| + Type* combined_type = expr->combined_type();
|
|
|
| CHECK_ALIVE(VisitForValue(expr->left()));
|
| CHECK_ALIVE(VisitForValue(expr->right()));
|
|
|
| - if (FLAG_emit_opt_code_positions) SetSourcePosition(expr->position());
|
| + if (FLAG_hydrogen_track_positions) SetSourcePosition(expr->position());
|
|
|
| HValue* right = Pop();
|
| HValue* left = Pop();
|
| @@ -9386,9 +9615,14 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
| return ast_context()->ReturnInstruction(result, expr->id());
|
| }
|
|
|
| + PushBeforeSimulateBehavior push_behavior =
|
| + ast_context()->IsEffect() ? NO_PUSH_BEFORE_SIMULATE
|
| + : PUSH_BEFORE_SIMULATE;
|
| HControlInstruction* compare = BuildCompareInstruction(
|
| op, left, right, left_type, right_type, combined_type,
|
| - expr->left()->position(), expr->right()->position(), expr->id());
|
| + ScriptPositionToSourcePosition(expr->left()->position()),
|
| + ScriptPositionToSourcePosition(expr->right()->position()),
|
| + push_behavior, expr->id());
|
| if (compare == NULL) return; // Bailed out.
|
| return ast_context()->ReturnControl(compare, expr->id());
|
| }
|
| @@ -9398,11 +9632,12 @@ HControlInstruction* HOptimizedGraphBuilder::BuildCompareInstruction(
|
| Token::Value op,
|
| HValue* left,
|
| HValue* right,
|
| - Handle<Type> left_type,
|
| - Handle<Type> right_type,
|
| - Handle<Type> combined_type,
|
| - int left_position,
|
| - int right_position,
|
| + Type* left_type,
|
| + Type* right_type,
|
| + Type* combined_type,
|
| + HSourcePosition left_position,
|
| + HSourcePosition right_position,
|
| + PushBeforeSimulateBehavior push_sim_result,
|
| BailoutId bailout_id) {
|
| // Cases handled below depend on collected type feedback. They should
|
| // soft deoptimize when there is no type feedback.
|
| @@ -9410,7 +9645,7 @@ HControlInstruction* HOptimizedGraphBuilder::BuildCompareInstruction(
|
| Add<HDeoptimize>("Insufficient type feedback for combined type "
|
| "of binary operation",
|
| Deoptimizer::SOFT);
|
| - combined_type = left_type = right_type = Type::Any(isolate());
|
| + combined_type = left_type = right_type = Type::Any(zone());
|
| }
|
|
|
| Representation left_rep = Representation::FromType(left_type);
|
| @@ -9427,7 +9662,7 @@ HControlInstruction* HOptimizedGraphBuilder::BuildCompareInstruction(
|
| AddCheckMap(operand_to_check, map);
|
| HCompareObjectEqAndBranch* result =
|
| New<HCompareObjectEqAndBranch>(left, right);
|
| - if (FLAG_emit_opt_code_positions) {
|
| + if (FLAG_hydrogen_track_positions) {
|
| result->set_operand_position(zone(), 0, left_position);
|
| result->set_operand_position(zone(), 1, right_position);
|
| }
|
| @@ -9467,9 +9702,13 @@ HControlInstruction* HOptimizedGraphBuilder::BuildCompareInstruction(
|
| result->set_observed_input_representation(1, left_rep);
|
| result->set_observed_input_representation(2, right_rep);
|
| if (result->HasObservableSideEffects()) {
|
| - Push(result);
|
| - AddSimulate(bailout_id, REMOVABLE_SIMULATE);
|
| - Drop(1);
|
| + if (push_sim_result == PUSH_BEFORE_SIMULATE) {
|
| + Push(result);
|
| + AddSimulate(bailout_id, REMOVABLE_SIMULATE);
|
| + Drop(1);
|
| + } else {
|
| + AddSimulate(bailout_id, REMOVABLE_SIMULATE);
|
| + }
|
| }
|
| // TODO(jkummerow): Can we make this more efficient?
|
| HBranch* branch = New<HBranch>(result);
|
| @@ -9478,7 +9717,7 @@ HControlInstruction* HOptimizedGraphBuilder::BuildCompareInstruction(
|
| HCompareNumericAndBranch* result =
|
| New<HCompareNumericAndBranch>(left, right, op);
|
| result->set_observed_input_representation(left_rep, right_rep);
|
| - if (FLAG_emit_opt_code_positions) {
|
| + if (FLAG_hydrogen_track_positions) {
|
| result->SetOperandPositions(zone(), left_position, right_position);
|
| }
|
| return result;
|
| @@ -9494,7 +9733,7 @@ void HOptimizedGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr,
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| ASSERT(expr->op() == Token::EQ || expr->op() == Token::EQ_STRICT);
|
| - if (!FLAG_emit_opt_code_positions) SetSourcePosition(expr->position());
|
| + if (!FLAG_hydrogen_track_positions) SetSourcePosition(expr->position());
|
| CHECK_ALIVE(VisitForValue(sub_expr));
|
| HValue* value = Pop();
|
| if (expr->op() == Token::EQ_STRICT) {
|
| @@ -9506,8 +9745,8 @@ void HOptimizedGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr,
|
| return ast_context()->ReturnControl(instr, expr->id());
|
| } else {
|
| ASSERT_EQ(Token::EQ, expr->op());
|
| - Handle<Type> type = expr->combined_type()->Is(Type::None())
|
| - ? Type::Any(isolate_) : expr->combined_type();
|
| + Type* type = expr->combined_type()->Is(Type::None())
|
| + ? Type::Any(zone()) : expr->combined_type();
|
| HIfContinuation continuation;
|
| BuildCompareNil(value, type, &continuation);
|
| return ast_context()->ReturnContinuation(&continuation, expr->id());
|
| @@ -9561,6 +9800,14 @@ HInstruction* HOptimizedGraphBuilder::BuildFastLiteral(
|
| if (elements_size > 0) {
|
| HValue* object_elements_size = Add<HConstant>(elements_size);
|
| if (boilerplate_object->HasFastDoubleElements()) {
|
| + // Allocation folding will not be able to fold |object| and
|
| + // |object_elements| together if they are pre-tenured.
|
| + if (pretenure_flag == TENURED) {
|
| + HConstant* empty_fixed_array = Add<HConstant>(
|
| + isolate()->factory()->empty_fixed_array());
|
| + Add<HStoreNamedField>(object, HObjectAccess::ForElementsPointer(),
|
| + empty_fixed_array);
|
| + }
|
| object_elements = Add<HAllocate>(object_elements_size, HType::JSObject(),
|
| pretenure_flag, FIXED_DOUBLE_ARRAY_TYPE, site_context->current());
|
| } else {
|
| @@ -9634,9 +9881,9 @@ void HOptimizedGraphBuilder::BuildEmitInObjectProperties(
|
| HInstruction* object,
|
| AllocationSiteUsageContext* site_context,
|
| PretenureFlag pretenure_flag) {
|
| - Handle<DescriptorArray> descriptors(
|
| - boilerplate_object->map()->instance_descriptors());
|
| - int limit = boilerplate_object->map()->NumberOfOwnDescriptors();
|
| + Handle<Map> boilerplate_map(boilerplate_object->map());
|
| + Handle<DescriptorArray> descriptors(boilerplate_map->instance_descriptors());
|
| + int limit = boilerplate_map->NumberOfOwnDescriptors();
|
|
|
| int copied_fields = 0;
|
| for (int i = 0; i < limit; i++) {
|
| @@ -9653,7 +9900,7 @@ void HOptimizedGraphBuilder::BuildEmitInObjectProperties(
|
| // The access for the store depends on the type of the boilerplate.
|
| HObjectAccess access = boilerplate_object->IsJSArray() ?
|
| HObjectAccess::ForJSArrayOffset(property_offset) :
|
| - HObjectAccess::ForJSObjectOffset(property_offset);
|
| + HObjectAccess::ForMapAndOffset(boilerplate_map, property_offset);
|
|
|
| if (value->IsJSObject()) {
|
| Handle<JSObject> value_object = Handle<JSObject>::cast(value);
|
| @@ -9681,8 +9928,12 @@ void HOptimizedGraphBuilder::BuildEmitInObjectProperties(
|
| Add<HStoreNamedField>(double_box, HObjectAccess::ForHeapNumberValue(),
|
| Add<HConstant>(value));
|
| value_instruction = double_box;
|
| - } else if (representation.IsSmi() && value->IsUninitialized()) {
|
| - value_instruction = graph()->GetConstant0();
|
| + } else if (representation.IsSmi()) {
|
| + value_instruction = value->IsUninitialized()
|
| + ? graph()->GetConstant0()
|
| + : Add<HConstant>(value);
|
| + // Ensure that value is stored as smi.
|
| + access = access.WithRepresentation(representation);
|
| } else {
|
| value_instruction = Add<HConstant>(value);
|
| }
|
| @@ -9697,7 +9948,8 @@ void HOptimizedGraphBuilder::BuildEmitInObjectProperties(
|
| for (int i = copied_fields; i < inobject_properties; i++) {
|
| ASSERT(boilerplate_object->IsJSObject());
|
| int property_offset = boilerplate_object->GetInObjectPropertyOffset(i);
|
| - HObjectAccess access = HObjectAccess::ForJSObjectOffset(property_offset);
|
| + HObjectAccess access =
|
| + HObjectAccess::ForMapAndOffset(boilerplate_map, property_offset);
|
| Add<HStoreNamedField>(object, access, value_instruction);
|
| }
|
| }
|
| @@ -10075,9 +10327,28 @@ void HOptimizedGraphBuilder::GenerateClassOf(CallRuntime* call) {
|
| void HOptimizedGraphBuilder::GenerateValueOf(CallRuntime* call) {
|
| ASSERT(call->arguments()->length() == 1);
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| - HValue* value = Pop();
|
| - HValueOf* result = New<HValueOf>(value);
|
| - return ast_context()->ReturnInstruction(result, call->id());
|
| + HValue* object = Pop();
|
| +
|
| + IfBuilder if_objectisvalue(this);
|
| + HValue* objectisvalue = if_objectisvalue.If<HHasInstanceTypeAndBranch>(
|
| + object, JS_VALUE_TYPE);
|
| + if_objectisvalue.Then();
|
| + {
|
| + // Return the actual value.
|
| + Push(Add<HLoadNamedField>(
|
| + object, objectisvalue,
|
| + HObjectAccess::ForObservableJSObjectOffset(
|
| + JSValue::kValueOffset)));
|
| + Add<HSimulate>(call->id(), FIXED_SIMULATE);
|
| + }
|
| + if_objectisvalue.Else();
|
| + {
|
| + // If the object is not a value return the object.
|
| + Push(object);
|
| + Add<HSimulate>(call->id(), FIXED_SIMULATE);
|
| + }
|
| + if_objectisvalue.End();
|
| + return ast_context()->ReturnValue(Pop());
|
| }
|
|
|
|
|
| @@ -10095,12 +10366,13 @@ void HOptimizedGraphBuilder::GenerateDateField(CallRuntime* call) {
|
| void HOptimizedGraphBuilder::GenerateOneByteSeqStringSetChar(
|
| CallRuntime* call) {
|
| ASSERT(call->arguments()->length() == 3);
|
| - CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| + // We need to follow the evaluation order of full codegen.
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(2)));
|
| + CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| + HValue* string = Pop();
|
| HValue* value = Pop();
|
| HValue* index = Pop();
|
| - HValue* string = Pop();
|
| Add<HSeqStringSetChar>(String::ONE_BYTE_ENCODING, string,
|
| index, value);
|
| Add<HSimulate>(call->id(), FIXED_SIMULATE);
|
| @@ -10111,12 +10383,13 @@ void HOptimizedGraphBuilder::GenerateOneByteSeqStringSetChar(
|
| void HOptimizedGraphBuilder::GenerateTwoByteSeqStringSetChar(
|
| CallRuntime* call) {
|
| ASSERT(call->arguments()->length() == 3);
|
| - CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| + // We need to follow the evaluation order of full codegen.
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(2)));
|
| + CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| + HValue* string = Pop();
|
| HValue* value = Pop();
|
| HValue* index = Pop();
|
| - HValue* string = Pop();
|
| Add<HSeqStringSetChar>(String::TWO_BYTE_ENCODING, string,
|
| index, value);
|
| Add<HSimulate>(call->id(), FIXED_SIMULATE);
|
| @@ -10130,31 +10403,33 @@ void HOptimizedGraphBuilder::GenerateSetValueOf(CallRuntime* call) {
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
|
| HValue* value = Pop();
|
| HValue* object = Pop();
|
| - // Check if object is a not a smi.
|
| - HBasicBlock* if_smi = graph()->CreateBasicBlock();
|
| - HBasicBlock* if_heap_object = graph()->CreateBasicBlock();
|
| - HBasicBlock* join = graph()->CreateBasicBlock();
|
| - FinishCurrentBlock(New<HIsSmiAndBranch>(object, if_smi, if_heap_object));
|
| - Goto(if_smi, join);
|
|
|
| // Check if object is a JSValue.
|
| - set_current_block(if_heap_object);
|
| - HHasInstanceTypeAndBranch* typecheck =
|
| - New<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);
|
| - FinishCurrentBlock(typecheck);
|
| - Goto(not_js_value, join);
|
| -
|
| - // Create in-object property store to kValueOffset.
|
| - set_current_block(if_js_value);
|
| - Add<HStoreNamedField>(object,
|
| - HObjectAccess::ForJSObjectOffset(JSValue::kValueOffset), value);
|
| - Goto(if_js_value, join);
|
| - join->SetJoinId(call->id());
|
| - set_current_block(join);
|
| + IfBuilder if_objectisvalue(this);
|
| + if_objectisvalue.If<HHasInstanceTypeAndBranch>(object, JS_VALUE_TYPE);
|
| + if_objectisvalue.Then();
|
| + {
|
| + // Create in-object property store to kValueOffset.
|
| + Add<HStoreNamedField>(object,
|
| + HObjectAccess::ForObservableJSObjectOffset(JSValue::kValueOffset),
|
| + value);
|
| + if (!ast_context()->IsEffect()) {
|
| + Push(value);
|
| + }
|
| + Add<HSimulate>(call->id(), FIXED_SIMULATE);
|
| + }
|
| + if_objectisvalue.Else();
|
| + {
|
| + // Nothing to do in this case.
|
| + if (!ast_context()->IsEffect()) {
|
| + Push(value);
|
| + }
|
| + Add<HSimulate>(call->id(), FIXED_SIMULATE);
|
| + }
|
| + if_objectisvalue.End();
|
| + if (!ast_context()->IsEffect()) {
|
| + Drop(1);
|
| + }
|
| return ast_context()->ReturnValue(value);
|
| }
|
|
|
| @@ -10259,10 +10534,14 @@ void HOptimizedGraphBuilder::GenerateRegExpExec(CallRuntime* call) {
|
| // Construct a RegExp exec result with two in-object properties.
|
| void HOptimizedGraphBuilder::GenerateRegExpConstructResult(CallRuntime* call) {
|
| ASSERT_EQ(3, call->arguments()->length());
|
| - CHECK_ALIVE(VisitArgumentList(call->arguments()));
|
| - HCallStub* result = New<HCallStub>(CodeStub::RegExpConstructResult, 3);
|
| - Drop(3);
|
| - return ast_context()->ReturnInstruction(result, call->id());
|
| + CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| + CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
|
| + CHECK_ALIVE(VisitForValue(call->arguments()->at(2)));
|
| + HValue* input = Pop();
|
| + HValue* index = Pop();
|
| + HValue* length = Pop();
|
| + HValue* result = BuildRegExpConstructResult(length, index, input);
|
| + return ast_context()->ReturnValue(result);
|
| }
|
|
|
|
|
| @@ -10277,7 +10556,7 @@ void HOptimizedGraphBuilder::GenerateNumberToString(CallRuntime* call) {
|
| ASSERT_EQ(1, call->arguments()->length());
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| HValue* number = Pop();
|
| - HValue* result = BuildNumberToString(number, Type::Number(isolate()));
|
| + HValue* result = BuildNumberToString(number, Type::Any(zone()));
|
| return ast_context()->ReturnValue(result);
|
| }
|
|
|
| @@ -10295,31 +10574,39 @@ void HOptimizedGraphBuilder::GenerateCallFunction(CallRuntime* call) {
|
|
|
| HValue* function = Pop();
|
|
|
| - // Branch for function proxies, or other non-functions.
|
| - HHasInstanceTypeAndBranch* typecheck =
|
| - New<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);
|
| - FinishCurrentBlock(typecheck);
|
| -
|
| - set_current_block(if_jsfunction);
|
| - HInstruction* invoke_result = Add<HInvokeFunction>(function, arg_count);
|
| - Drop(arg_count);
|
| - Push(invoke_result);
|
| - Goto(if_jsfunction, join);
|
| -
|
| - set_current_block(if_nonfunction);
|
| - HInstruction* call_result = Add<HCallFunction>(function, arg_count);
|
| - Drop(arg_count);
|
| - Push(call_result);
|
| - Goto(if_nonfunction, join);
|
| + IfBuilder if_is_jsfunction(this);
|
| + if_is_jsfunction.If<HHasInstanceTypeAndBranch>(function, JS_FUNCTION_TYPE);
|
|
|
| - set_current_block(join);
|
| - join->SetJoinId(call->id());
|
| - return ast_context()->ReturnValue(Pop());
|
| + if_is_jsfunction.Then();
|
| + {
|
| + HInstruction* invoke_result =
|
| + Add<HInvokeFunction>(function, arg_count);
|
| + Drop(arg_count);
|
| + if (!ast_context()->IsEffect()) {
|
| + Push(invoke_result);
|
| + }
|
| + Add<HSimulate>(call->id(), FIXED_SIMULATE);
|
| + }
|
| +
|
| + if_is_jsfunction.Else();
|
| + {
|
| + HInstruction* call_result =
|
| + Add<HCallFunction>(function, arg_count);
|
| + Drop(arg_count);
|
| + if (!ast_context()->IsEffect()) {
|
| + Push(call_result);
|
| + }
|
| + Add<HSimulate>(call->id(), FIXED_SIMULATE);
|
| + }
|
| + if_is_jsfunction.End();
|
| +
|
| + if (ast_context()->IsEffect()) {
|
| + // EffectContext::ReturnValue ignores the value, so we can just pass
|
| + // 'undefined' (as we do not have the call result anymore).
|
| + return ast_context()->ReturnValue(graph()->GetConstantUndefined());
|
| + } else {
|
| + return ast_context()->ReturnValue(Pop());
|
| + }
|
| }
|
|
|
|
|
| @@ -10353,12 +10640,6 @@ void HOptimizedGraphBuilder::GenerateMathSqrt(CallRuntime* call) {
|
| }
|
|
|
|
|
| -// Check whether two RegExps are equivalent
|
| -void HOptimizedGraphBuilder::GenerateIsRegExpEquivalent(CallRuntime* call) {
|
| - return Bailout(kInlinedRuntimeFunctionIsRegExpEquivalent);
|
| -}
|
| -
|
| -
|
| void HOptimizedGraphBuilder::GenerateGetCachedArrayIndex(CallRuntime* call) {
|
| ASSERT(call->arguments()->length() == 1);
|
| CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
| @@ -10691,7 +10972,10 @@ void HTracer::TraceCompilation(CompilationInfo* info) {
|
| if (info->IsOptimizing()) {
|
| Handle<String> name = info->function()->debug_name();
|
| PrintStringProperty("name", name->ToCString().get());
|
| - PrintStringProperty("method", name->ToCString().get());
|
| + PrintIndent();
|
| + trace_.Add("method \"%s:%d\"\n",
|
| + name->ToCString().get(),
|
| + info->optimization_id());
|
| } else {
|
| CodeStub::Major major_key = info->code_stub()->MajorKey();
|
| PrintStringProperty("name", CodeStub::MajorName(major_key, false));
|
| @@ -10805,14 +11089,22 @@ void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) {
|
| Tag HIR_tag(this, "HIR");
|
| for (HInstructionIterator it(current); !it.Done(); it.Advance()) {
|
| HInstruction* instruction = it.Current();
|
| - int bci = FLAG_emit_opt_code_positions && instruction->has_position() ?
|
| - instruction->position() : 0;
|
| int uses = instruction->UseCount();
|
| PrintIndent();
|
| - trace_.Add("%d %d ", bci, uses);
|
| + trace_.Add("0 %d ", uses);
|
| instruction->PrintNameTo(&trace_);
|
| trace_.Add(" ");
|
| instruction->PrintTo(&trace_);
|
| + if (FLAG_hydrogen_track_positions &&
|
| + instruction->has_position() &&
|
| + instruction->position().raw() != 0) {
|
| + const HSourcePosition pos = instruction->position();
|
| + trace_.Add(" pos:");
|
| + if (pos.inlining_id() != 0) {
|
| + trace_.Add("%d_", pos.inlining_id());
|
| + }
|
| + trace_.Add("%d", pos.position());
|
| + }
|
| trace_.Add(" <|@\n");
|
| }
|
| }
|
|
|