From 26bc8db33f15a365e22f23229d0700d70ac0d560 Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Tue, 27 Aug 2013 15:18:12 -0700 Subject: [PATCH] v8: upgrade to 3.20.17 --- deps/v8/ChangeLog | 33 + deps/v8/include/v8-profiler.h | 20 +- deps/v8/include/v8.h | 16 +- deps/v8/src/api.cc | 37 +- deps/v8/src/arm/lithium-arm.cc | 96 +-- deps/v8/src/arm/lithium-arm.h | 68 +- deps/v8/src/arm/lithium-codegen-arm.cc | 279 ++++----- deps/v8/src/arm/lithium-codegen-arm.h | 29 +- deps/v8/src/arm/macro-assembler-arm.cc | 15 +- deps/v8/src/assert-scope.h | 9 + deps/v8/src/ast.cc | 3 +- deps/v8/src/ast.h | 29 +- deps/v8/src/code-stubs-hydrogen.cc | 21 +- deps/v8/src/code-stubs.cc | 3 + deps/v8/src/compiler.cc | 19 +- deps/v8/src/compiler.h | 17 +- deps/v8/src/cpu-profiler.cc | 2 +- deps/v8/src/cpu-profiler.h | 2 +- deps/v8/src/debug.cc | 7 + deps/v8/src/deoptimizer.cc | 297 +++++++-- deps/v8/src/deoptimizer.h | 57 +- deps/v8/src/effects.h | 361 +++++++++++ deps/v8/src/execution.cc | 4 +- deps/v8/src/extensions/i18n/collator.cc | 366 ----------- deps/v8/src/extensions/i18n/collator.js | 11 +- deps/v8/src/extensions/i18n/i18n-extension.cc | 18 - deps/v8/src/extensions/i18n/number-format.cc | 418 ------------- deps/v8/src/extensions/i18n/number-format.js | 16 +- deps/v8/src/factory.cc | 13 +- deps/v8/src/factory.h | 6 +- deps/v8/src/flag-definitions.h | 2 + deps/v8/src/global-handles.cc | 529 +++++----------- deps/v8/src/global-handles.h | 64 +- deps/v8/src/heap.cc | 49 +- deps/v8/src/heap.h | 9 +- deps/v8/src/hydrogen-dce.cc | 4 +- deps/v8/src/hydrogen-escape-analysis.cc | 230 +++++++ deps/v8/src/hydrogen-escape-analysis.h | 35 +- deps/v8/src/hydrogen-gvn.h | 2 +- deps/v8/src/hydrogen-instructions.cc | 222 ++----- deps/v8/src/hydrogen-instructions.h | 197 +++--- deps/v8/src/hydrogen-mark-deoptimize.cc | 29 +- deps/v8/src/hydrogen-mark-deoptimize.h | 12 + deps/v8/src/hydrogen-osr.cc | 1 + deps/v8/src/hydrogen-representation-changes.cc | 10 +- deps/v8/src/hydrogen.cc | 283 ++++++--- deps/v8/src/hydrogen.h | 79 ++- deps/v8/src/i18n.cc | 691 ++++++++++++++++++++- deps/v8/src/i18n.h | 50 ++ deps/v8/src/ia32/code-stubs-ia32.cc | 55 +- deps/v8/src/ia32/lithium-codegen-ia32.cc | 342 ++++------ deps/v8/src/ia32/lithium-codegen-ia32.h | 22 +- deps/v8/src/ia32/lithium-ia32.cc | 122 ++-- deps/v8/src/ia32/lithium-ia32.h | 63 +- deps/v8/src/ia32/macro-assembler-ia32.cc | 69 +- deps/v8/src/ia32/macro-assembler-ia32.h | 13 +- deps/v8/src/ia32/stub-cache-ia32.cc | 2 + deps/v8/src/isolate.cc | 1 - deps/v8/src/isolate.h | 17 +- deps/v8/src/json-stringifier.h | 1 + deps/v8/src/lithium.h | 48 +- deps/v8/src/liveedit.cc | 1 + deps/v8/src/mips/codegen-mips.cc | 2 +- deps/v8/src/mips/lithium-codegen-mips.cc | 180 ++---- deps/v8/src/mips/lithium-codegen-mips.h | 19 +- deps/v8/src/mips/lithium-mips.cc | 91 +-- deps/v8/src/mips/lithium-mips.h | 51 +- deps/v8/src/mips/macro-assembler-mips.cc | 32 +- deps/v8/src/mips/macro-assembler-mips.h | 14 - deps/v8/src/objects-printer.cc | 84 +-- deps/v8/src/objects.cc | 17 +- deps/v8/src/objects.h | 6 +- deps/v8/src/optimizing-compiler-thread.cc | 82 ++- deps/v8/src/optimizing-compiler-thread.h | 11 +- deps/v8/src/profile-generator-inl.h | 2 + deps/v8/src/profile-generator.cc | 6 +- deps/v8/src/profile-generator.h | 2 + deps/v8/src/runtime.cc | 231 ++++++- deps/v8/src/runtime.h | 9 + deps/v8/src/sampler.cc | 3 +- deps/v8/src/splay-tree-inl.h | 9 +- deps/v8/src/splay-tree.h | 14 +- deps/v8/src/types.h | 5 + deps/v8/src/typing.cc | 240 +++++-- deps/v8/src/typing.h | 17 + deps/v8/src/v8globals.h | 3 +- deps/v8/src/version.cc | 4 +- deps/v8/src/x64/lithium-codegen-x64.cc | 285 +++------ deps/v8/src/x64/lithium-codegen-x64.h | 25 +- deps/v8/src/x64/lithium-x64.cc | 96 +-- deps/v8/src/x64/lithium-x64.h | 67 +- deps/v8/src/x64/stub-cache-x64.cc | 13 +- deps/v8/src/zone-inl.h | 6 + deps/v8/src/zone.h | 6 +- deps/v8/test/cctest/cctest.h | 53 -- deps/v8/test/cctest/test-api.cc | 14 +- deps/v8/test/cctest/test-cpu-profiler.cc | 55 ++ deps/v8/test/cctest/test-deoptimization.cc | 22 +- deps/v8/test/cctest/test-global-handles.cc | 155 ----- deps/v8/test/cctest/test-heap.cc | 1 + deps/v8/test/cctest/test-log-stack-tracer.cc | 2 +- deps/v8/test/cctest/test-strings.cc | 52 ++ deps/v8/test/intl/intl.status | 3 + deps/v8/test/mjsunit/compiler/escape-analysis.js | 134 ++++ deps/v8/test/mjsunit/constant-compare-nil-value.js | 42 ++ deps/v8/test/mjsunit/debug-evaluate-const.js | 121 ++++ deps/v8/test/mjsunit/debug-evaluate-locals.js | 2 +- deps/v8/test/mjsunit/debug-stepin-positions.js | 142 +++++ deps/v8/test/mjsunit/harmony/object-observe.js | 5 +- deps/v8/test/mjsunit/load_poly_effect.js | 47 ++ deps/v8/test/mjsunit/mjsunit.status | 3 + deps/v8/test/mjsunit/object-literal-overwrite.js | 18 +- .../regress/json-stringifier-emptyhandle.js | 44 ++ deps/v8/test/mjsunit/regress/regress-2836.js | 38 ++ .../test/mjsunit/regress/regress-convert-hole2.js | 86 +++ .../test/mjsunit/regress/regress-crbug-272564.js | 49 ++ .../mjsunit/regress/regress-et-clobbers-doubles.js | 39 ++ deps/v8/test/mjsunit/regress/regress-freeze.js | 46 ++ .../regress/regress-hoist-load-named-field.js | 66 ++ .../mjsunit/regress/regress-map-invalidation-1.js | 48 ++ .../mjsunit/regress/regress-map-invalidation-2.js | 49 ++ .../mjsunit/regress/regress-phi-truncation.js} | 81 ++- .../regress-prepare-break-while-recompile.js | 62 ++ .../regress/regress-smi-math-floor-round.js} | 62 +- deps/v8/test/webkit/webkit.status | 3 - deps/v8/tools/gcmole/Makefile | 2 +- deps/v8/tools/gcmole/bootstrap.sh | 126 ++++ deps/v8/tools/gcmole/gcmole.lua | 3 + deps/v8/tools/gyp/v8.gyp | 5 +- deps/v8/tools/tickprocessor.js | 3 +- deps/v8/tools/v8heapconst.py | 9 +- 131 files changed, 5439 insertions(+), 3411 deletions(-) create mode 100644 deps/v8/src/effects.h delete mode 100644 deps/v8/src/extensions/i18n/collator.cc delete mode 100644 deps/v8/src/extensions/i18n/number-format.cc create mode 100644 deps/v8/test/mjsunit/compiler/escape-analysis.js create mode 100644 deps/v8/test/mjsunit/constant-compare-nil-value.js create mode 100644 deps/v8/test/mjsunit/debug-evaluate-const.js create mode 100644 deps/v8/test/mjsunit/debug-stepin-positions.js create mode 100644 deps/v8/test/mjsunit/load_poly_effect.js create mode 100644 deps/v8/test/mjsunit/regress/json-stringifier-emptyhandle.js create mode 100644 deps/v8/test/mjsunit/regress/regress-2836.js create mode 100644 deps/v8/test/mjsunit/regress/regress-convert-hole2.js create mode 100644 deps/v8/test/mjsunit/regress/regress-crbug-272564.js create mode 100644 deps/v8/test/mjsunit/regress/regress-et-clobbers-doubles.js create mode 100644 deps/v8/test/mjsunit/regress/regress-freeze.js create mode 100644 deps/v8/test/mjsunit/regress/regress-hoist-load-named-field.js create mode 100644 deps/v8/test/mjsunit/regress/regress-map-invalidation-1.js create mode 100644 deps/v8/test/mjsunit/regress/regress-map-invalidation-2.js rename deps/v8/{src/extensions/i18n/collator.h => test/mjsunit/regress/regress-phi-truncation.js} (58%) create mode 100644 deps/v8/test/mjsunit/regress/regress-prepare-break-while-recompile.js rename deps/v8/{src/extensions/i18n/number-format.h => test/mjsunit/regress/regress-smi-math-floor-round.js} (57%) create mode 100755 deps/v8/tools/gcmole/bootstrap.sh diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index b19fda2..d824cec 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,3 +1,36 @@ +2013-08-14: Version 3.20.17 + + Fixed Math.round/floor that had bogus Smi representation + (Chromium issue 272564) + + Performance and stability improvements on all platforms. + + +2013-08-13: Version 3.20.16 + + Fixed bug in HPhi::SimplifyConstantInput (Chromium issue 269679) + + Fixed gcmole bugs in i18n code (issue 2745) + + ia32: Calls to the TranscendentalCacheStub must ensure that esi is + set (issue 2827) + + Made sure polymorphic element access creates non-replaying + phis. (issue 2815) + + Allowed HPhis to have an invalid merge index. (issue 2815) + + Fixed smi-based math floor. (Chromium issue 270268) + + Deprecated self and total time getters and total sample count + getter on CpuProfileNode. (Chromium issue 267595) + + Fixed Object.freeze, Object.observe wrt CountOperation and + CompoundAssignment. (issue 2774,2779) + + Performance and stability improvements on all platforms. + + 2013-08-07: Version 3.20.14 Exposed eternal handle api. diff --git a/deps/v8/include/v8-profiler.h b/deps/v8/include/v8-profiler.h index 1d7b70d..e538f4a 100644 --- a/deps/v8/include/v8-profiler.h +++ b/deps/v8/include/v8-profiler.h @@ -61,20 +61,27 @@ class V8_EXPORT CpuProfileNode { * Returns total (self + children) execution time of the function, * in milliseconds, estimated by samples count. */ - double GetTotalTime() const; + V8_DEPRECATED(double GetTotalTime() const); /** * Returns self execution time of the function, in milliseconds, * estimated by samples count. */ - double GetSelfTime() const; + V8_DEPRECATED(double GetSelfTime() const); /** Returns the count of samples where function exists. */ - double GetTotalSamplesCount() const; + V8_DEPRECATED(double GetTotalSamplesCount() const); - /** Returns the count of samples where function was currently executing. */ + /** DEPRECATED. Please use GetHitCount instead. + * Returns the count of samples where function was currently executing. + */ double GetSelfSamplesCount() const; + /** + * Returns the count of samples where the function was currently executing. + */ + unsigned GetHitCount() const; + /** Returns function entry UID. */ unsigned GetCallUid() const; @@ -192,6 +199,11 @@ class V8_EXPORT CpuProfiler { */ void DeleteAllCpuProfiles(); + /** + * Tells the profiler whether the embedder is idle. + */ + void SetIdle(bool is_idle); + private: CpuProfiler(); ~CpuProfiler(); diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 4b31e87..3252602 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -1273,13 +1273,13 @@ class V8_EXPORT StackFrame { class V8_EXPORT JSON { public: /** - * Tries to parse the string |json_string| and returns it as object if + * Tries to parse the string |json_string| and returns it as value if * successful. * * \param json_string The string to parse. - * \return The corresponding object if successfully parsed. + * \return The corresponding value if successfully parsed. */ - static Local Parse(Local json_string); + static Local Parse(Local json_string); }; @@ -5544,14 +5544,14 @@ class Internals { V8_INLINE(static uint8_t GetNodeFlag(internal::Object** obj, int shift)) { uint8_t* addr = reinterpret_cast(obj) + kNodeFlagsOffset; - return *addr & (1 << shift); + return *addr & static_cast(1U << shift); } V8_INLINE(static void UpdateNodeFlag(internal::Object** obj, bool value, int shift)) { uint8_t* addr = reinterpret_cast(obj) + kNodeFlagsOffset; - uint8_t mask = 1 << shift; - *addr = (*addr & ~mask) | (value << shift); + uint8_t mask = static_cast(1 << shift); + *addr = static_cast((*addr & ~mask) | (value << shift)); } V8_INLINE(static uint8_t GetNodeState(internal::Object** obj)) { @@ -5562,7 +5562,7 @@ class Internals { V8_INLINE(static void UpdateNodeState(internal::Object** obj, uint8_t value)) { uint8_t* addr = reinterpret_cast(obj) + kNodeFlagsOffset; - *addr = (*addr & ~kNodeStateMask) | value; + *addr = static_cast((*addr & ~kNodeStateMask) | value); } V8_INLINE(static void SetEmbedderData(v8::Isolate* isolate, void* data)) { @@ -5927,7 +5927,7 @@ void ReturnValue::Set(uint32_t i) { TYPE_CHECK(T, Integer); typedef internal::Internals I; // Can't simply use INT32_MAX here for whatever reason. - bool fits_into_int32_t = (i & (1 << 31)) == 0; + bool fits_into_int32_t = (i & (1U << 31)) == 0; if (V8_LIKELY(fits_into_int32_t)) { Set(static_cast(i)); return; diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index e04fbef..eb2ffcf 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -781,7 +781,6 @@ void Context::Exit() { i::Context* last_context = isolate->handle_scope_implementer()->RestoreContext(); isolate->set_context(last_context); - isolate->set_context_exit_happened(true); } @@ -2620,7 +2619,7 @@ bool StackFrame::IsConstructor() const { // --- J S O N --- -Local JSON::Parse(Local json_string) { +Local JSON::Parse(Local json_string) { i::Isolate* isolate = i::Isolate::Current(); EnsureInitializedForIsolate(isolate, "v8::JSON::Parse"); ENTER_V8(isolate); @@ -2637,7 +2636,7 @@ Local JSON::Parse(Local json_string) { has_pending_exception = result.is_null(); EXCEPTION_BAILOUT_CHECK(isolate, Local()); return Utils::ToLocal( - i::Handle::cast(scope.CloseAndEscape(result))); + i::Handle::cast(scope.CloseAndEscape(result))); } @@ -7478,8 +7477,6 @@ Handle CpuProfileNode::GetFunctionName() const { int CpuProfileNode::GetScriptId() const { - i::Isolate* isolate = i::Isolate::Current(); - IsDeadCheck(isolate, "v8::CpuProfileNode::GetScriptId"); const i::ProfileNode* node = reinterpret_cast(this); const i::CodeEntry* entry = node->entry(); return entry->script_id(); @@ -7496,8 +7493,6 @@ Handle CpuProfileNode::GetScriptResourceName() const { int CpuProfileNode::GetLineNumber() const { - i::Isolate* isolate = i::Isolate::Current(); - IsDeadCheck(isolate, "v8::CpuProfileNode::GetLineNumber"); return reinterpret_cast(this)->entry()->line_number(); } @@ -7530,9 +7525,12 @@ double CpuProfileNode::GetSelfSamplesCount() const { } +unsigned CpuProfileNode::GetHitCount() const { + return reinterpret_cast(this)->self_ticks(); +} + + unsigned CpuProfileNode::GetCallUid() const { - i::Isolate* isolate = i::Isolate::Current(); - IsDeadCheck(isolate, "v8::CpuProfileNode::GetCallUid"); return reinterpret_cast(this)->entry()->GetCallUid(); } @@ -7543,15 +7541,11 @@ unsigned CpuProfileNode::GetNodeId() const { int CpuProfileNode::GetChildrenCount() const { - i::Isolate* isolate = i::Isolate::Current(); - IsDeadCheck(isolate, "v8::CpuProfileNode::GetChildrenCount"); return reinterpret_cast(this)->children()->length(); } const CpuProfileNode* CpuProfileNode::GetChild(int index) const { - i::Isolate* isolate = i::Isolate::Current(); - IsDeadCheck(isolate, "v8::CpuProfileNode::GetChild"); const i::ProfileNode* child = reinterpret_cast(this)->children()->at(index); return reinterpret_cast(child); @@ -7572,8 +7566,6 @@ void CpuProfile::Delete() { unsigned CpuProfile::GetUid() const { - i::Isolate* isolate = i::Isolate::Current(); - IsDeadCheck(isolate, "v8::CpuProfile::GetUid"); return reinterpret_cast(this)->uid(); } @@ -7588,8 +7580,6 @@ Handle CpuProfile::GetTitle() const { const CpuProfileNode* CpuProfile::GetTopDownRoot() const { - i::Isolate* isolate = i::Isolate::Current(); - IsDeadCheck(isolate, "v8::CpuProfile::GetTopDownRoot"); const i::CpuProfile* profile = reinterpret_cast(this); return reinterpret_cast(profile->top_down()->root()); } @@ -7647,6 +7637,19 @@ void CpuProfiler::DeleteAllCpuProfiles() { } +void CpuProfiler::SetIdle(bool is_idle) { + i::Isolate* isolate = reinterpret_cast(this)->isolate(); + i::StateTag state = isolate->current_vm_state(); + ASSERT(state == i::EXTERNAL || state == i::IDLE); + if (isolate->js_entry_sp() != NULL) return; + if (is_idle) { + isolate->set_current_vm_state(i::IDLE); + } else if (state == i::IDLE) { + isolate->set_current_vm_state(i::EXTERNAL); + } +} + + static i::HeapGraphEdge* ToInternal(const HeapGraphEdge* edge) { return const_cast( reinterpret_cast(edge)); diff --git a/deps/v8/src/arm/lithium-arm.cc b/deps/v8/src/arm/lithium-arm.cc index 43f0fd3..998b73b 100644 --- a/deps/v8/src/arm/lithium-arm.cc +++ b/deps/v8/src/arm/lithium-arm.cc @@ -593,8 +593,10 @@ LInstruction* LChunkBuilder::DefineFixedDouble( LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { HEnvironment* hydrogen_env = current_block_->last_environment(); int argument_index_accumulator = 0; + ZoneList objects_to_materialize(0, zone()); instr->set_environment(CreateEnvironment(hydrogen_env, - &argument_index_accumulator)); + &argument_index_accumulator, + &objects_to_materialize)); return instr; } @@ -813,7 +815,7 @@ void LChunkBuilder::DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block) { HEnvironment* last_environment = pred->last_environment(); for (int i = 0; i < block->phis()->length(); ++i) { HPhi* phi = block->phis()->at(i); - if (phi->merged_index() < last_environment->length()) { + if (phi->HasMergedIndex()) { last_environment->SetValueAt(phi->merged_index(), phi); } } @@ -883,6 +885,7 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { } #endif + instr->set_position(position_); if (FLAG_stress_pointer_maps && !instr->HasPointerMap()) { instr = AssignPointerMap(instr); } @@ -898,11 +901,13 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { LEnvironment* LChunkBuilder::CreateEnvironment( HEnvironment* hydrogen_env, - int* argument_index_accumulator) { + int* argument_index_accumulator, + ZoneList* objects_to_materialize) { if (hydrogen_env == NULL) return NULL; - LEnvironment* outer = - CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); + LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(), + argument_index_accumulator, + objects_to_materialize); BailoutId ast_id = hydrogen_env->ast_id(); ASSERT(!ast_id.IsNone() || hydrogen_env->frame_type() != JS_FUNCTION); @@ -917,16 +922,16 @@ LEnvironment* LChunkBuilder::CreateEnvironment( outer, hydrogen_env->entry(), zone()); - bool needs_arguments_object_materialization = false; int argument_index = *argument_index_accumulator; + int object_index = objects_to_materialize->length(); for (int i = 0; i < hydrogen_env->length(); ++i) { if (hydrogen_env->is_special_index(i)) continue; + LOperand* op; HValue* value = hydrogen_env->values()->at(i); - LOperand* op = NULL; - if (value->IsArgumentsObject()) { - needs_arguments_object_materialization = true; - op = NULL; + if (value->IsArgumentsObject() || value->IsCapturedObject()) { + objects_to_materialize->Add(value, zone()); + op = LEnvironment::materialization_marker(); } else if (value->IsPushArgument()) { op = new(zone()) LArgument(argument_index++); } else { @@ -937,15 +942,33 @@ LEnvironment* LChunkBuilder::CreateEnvironment( value->CheckFlag(HInstruction::kUint32)); } - if (needs_arguments_object_materialization) { - HArgumentsObject* arguments = hydrogen_env->entry() == NULL - ? graph()->GetArgumentsObject() - : hydrogen_env->entry()->arguments_object(); - ASSERT(arguments->IsLinked()); - for (int i = 1; i < arguments->arguments_count(); ++i) { - HValue* value = arguments->arguments_values()->at(i); - ASSERT(!value->IsArgumentsObject() && !value->IsPushArgument()); - LOperand* op = UseAny(value); + for (int i = object_index; i < objects_to_materialize->length(); ++i) { + HValue* object_to_materialize = objects_to_materialize->at(i); + int previously_materialized_object = -1; + for (int prev = 0; prev < i; ++prev) { + if (objects_to_materialize->at(prev) == objects_to_materialize->at(i)) { + previously_materialized_object = prev; + break; + } + } + int length = object_to_materialize->OperandCount(); + bool is_arguments = object_to_materialize->IsArgumentsObject(); + if (previously_materialized_object >= 0) { + result->AddDuplicateObject(previously_materialized_object); + continue; + } else { + result->AddNewObject(is_arguments ? length - 1 : length, is_arguments); + } + for (int i = is_arguments ? 1 : 0; i < length; ++i) { + LOperand* op; + HValue* value = object_to_materialize->OperandAt(i); + if (value->IsArgumentsObject() || value->IsCapturedObject()) { + objects_to_materialize->Add(value, zone()); + op = LEnvironment::materialization_marker(); + } else { + ASSERT(!value->IsPushArgument()); + op = UseAny(value); + } result->AddValue(op, value->representation(), value->CheckFlag(HInstruction::kUint32)); @@ -1686,9 +1709,8 @@ LInstruction* LChunkBuilder::DoCompareNumericAndBranch( HCompareNumericAndBranch* instr) { Representation r = instr->representation(); if (r.IsSmiOrInteger32()) { - ASSERT(instr->left()->representation().IsSmiOrInteger32()); - ASSERT(instr->left()->representation().Equals( - instr->right()->representation())); + ASSERT(instr->left()->representation().Equals(r)); + ASSERT(instr->right()->representation().Equals(r)); LOperand* left = UseRegisterOrConstantAtStart(instr->left()); LOperand* right = UseRegisterOrConstantAtStart(instr->right()); return new(zone()) LCompareNumericAndBranch(left, right); @@ -1711,6 +1733,13 @@ LInstruction* LChunkBuilder::DoCompareObjectEqAndBranch( } +LInstruction* LChunkBuilder::DoCompareHoleAndBranch( + HCompareHoleAndBranch* instr) { + LOperand* object = UseRegisterAtStart(instr->object()); + return new(zone()) LCmpHoleAndBranch(object); +} + + LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) { ASSERT(instr->value()->representation().IsTagged()); LOperand* value = UseRegisterAtStart(instr->value()); @@ -2124,23 +2153,6 @@ LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) { } -LInstruction* LChunkBuilder::DoLoadNamedFieldPolymorphic( - HLoadNamedFieldPolymorphic* instr) { - ASSERT(instr->representation().IsTagged()); - if (instr->need_generic()) { - LOperand* obj = UseFixed(instr->object(), r0); - LLoadNamedFieldPolymorphic* result = - new(zone()) LLoadNamedFieldPolymorphic(obj); - return MarkAsCall(DefineFixed(result, r0), instr); - } else { - LOperand* obj = UseRegisterAtStart(instr->object()); - LLoadNamedFieldPolymorphic* result = - new(zone()) LLoadNamedFieldPolymorphic(obj); - return AssignEnvironment(DefineAsRegister(result)); - } -} - - LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) { LOperand* object = UseFixed(instr->object(), r0); LInstruction* result = DefineFixed(new(zone()) LLoadNamedGeneric(object), r0); @@ -2437,6 +2449,12 @@ LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) { } +LInstruction* LChunkBuilder::DoCapturedObject(HCapturedObject* instr) { + // There are no real uses of a captured object. + return NULL; +} + + LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) { info()->MarkAsRequiresFrame(); LOperand* args = UseRegister(instr->arguments()); diff --git a/deps/v8/src/arm/lithium-arm.h b/deps/v8/src/arm/lithium-arm.h index 7ce907a..d81dc0f 100644 --- a/deps/v8/src/arm/lithium-arm.h +++ b/deps/v8/src/arm/lithium-arm.h @@ -74,6 +74,7 @@ class LCodeGen; V(ClassOfTestAndBranch) \ V(CompareNumericAndBranch) \ V(CmpObjectEqAndBranch) \ + V(CmpHoleAndBranch) \ V(CmpMapAndBranch) \ V(CmpT) \ V(ConstantD) \ @@ -126,7 +127,6 @@ class LCodeGen; V(LoadKeyed) \ V(LoadKeyedGeneric) \ V(LoadNamedField) \ - V(LoadNamedFieldPolymorphic) \ V(LoadNamedGeneric) \ V(MapEnumLength) \ V(MathAbs) \ @@ -208,9 +208,12 @@ class LCodeGen; class LInstruction: public ZoneObject { public: LInstruction() - : environment_(NULL), - hydrogen_value_(NULL), - is_call_(false) { } + : environment_(NULL), + hydrogen_value_(NULL), + bit_field_(IsCallBits::encode(false)) { + set_position(RelocInfo::kNoPosition); + } + virtual ~LInstruction() { } virtual void CompileToNative(LCodeGen* generator) = 0; @@ -249,20 +252,30 @@ class LInstruction: public ZoneObject { LPointerMap* pointer_map() const { return pointer_map_.get(); } bool HasPointerMap() const { return pointer_map_.is_set(); } + // The 31 bits PositionBits is used to store the int position value. And the + // position value may be RelocInfo::kNoPosition (-1). The accessor always + // +1/-1 so that the encoded value of position in bit_field_ is always >= 0 + // and can fit into the 31 bits PositionBits. + void set_position(int pos) { + bit_field_ = PositionBits::update(bit_field_, pos + 1); + } + int position() { return PositionBits::decode(bit_field_) - 1; } + void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; } HValue* hydrogen_value() const { return hydrogen_value_; } virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { } - void MarkAsCall() { is_call_ = true; } + void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); } + bool IsCall() const { return IsCallBits::decode(bit_field_); } // Interface to the register allocator and iterators. - bool ClobbersTemps() const { return is_call_; } - bool ClobbersRegisters() const { return is_call_; } - bool ClobbersDoubleRegisters() const { return is_call_; } + bool ClobbersTemps() const { return IsCall(); } + bool ClobbersRegisters() const { return IsCall(); } + bool ClobbersDoubleRegisters() const { return IsCall(); } // Interface to the register allocator and iterators. - bool IsMarkedAsCall() const { return is_call_; } + bool IsMarkedAsCall() const { return IsCall(); } virtual bool HasResult() const = 0; virtual LOperand* result() const = 0; @@ -286,10 +299,13 @@ class LInstruction: public ZoneObject { virtual int TempCount() = 0; virtual LOperand* TempAt(int i) = 0; + class IsCallBits: public BitField {}; + class PositionBits: public BitField {}; + LEnvironment* environment_; SetOncePointer pointer_map_; HValue* hydrogen_value_; - bool is_call_; + int bit_field_; }; @@ -879,12 +895,24 @@ class LCmpObjectEqAndBranch: public LControlInstruction<2, 0> { LOperand* left() { return inputs_[0]; } LOperand* right() { return inputs_[1]; } - DECLARE_CONCRETE_INSTRUCTION(CmpObjectEqAndBranch, - "cmp-object-eq-and-branch") + DECLARE_CONCRETE_INSTRUCTION(CmpObjectEqAndBranch, "cmp-object-eq-and-branch") DECLARE_HYDROGEN_ACCESSOR(CompareObjectEqAndBranch) }; +class LCmpHoleAndBranch: public LControlInstruction<1, 0> { + public: + explicit LCmpHoleAndBranch(LOperand* object) { + inputs_[0] = object; + } + + LOperand* object() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(CmpHoleAndBranch, "cmp-hole-and-branch") + DECLARE_HYDROGEN_ACCESSOR(CompareHoleAndBranch) +}; + + class LIsObjectAndBranch: public LControlInstruction<1, 1> { public: LIsObjectAndBranch(LOperand* value, LOperand* temp) { @@ -1511,19 +1539,6 @@ class LLoadNamedField: public LTemplateInstruction<1, 1, 0> { }; -class LLoadNamedFieldPolymorphic: public LTemplateInstruction<1, 1, 0> { - public: - explicit LLoadNamedFieldPolymorphic(LOperand* object) { - inputs_[0] = object; - } - - LOperand* object() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field-polymorphic") - DECLARE_HYDROGEN_ACCESSOR(LoadNamedFieldPolymorphic) -}; - - class LLoadNamedGeneric: public LTemplateInstruction<1, 1, 0> { public: explicit LLoadNamedGeneric(LOperand* object) { @@ -2718,7 +2733,8 @@ class LChunkBuilder BASE_EMBEDDED { CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY); LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env, - int* argument_index_accumulator); + int* argument_index_accumulator, + ZoneList* objects_to_materialize); void VisitInstruction(HInstruction* current); diff --git a/deps/v8/src/arm/lithium-codegen-arm.cc b/deps/v8/src/arm/lithium-codegen-arm.cc index 0b704d0..9ec80f8 100644 --- a/deps/v8/src/arm/lithium-codegen-arm.cc +++ b/deps/v8/src/arm/lithium-codegen-arm.cc @@ -274,6 +274,8 @@ bool LCodeGen::GenerateBody() { instr->Mnemonic()); } + RecordAndUpdatePosition(instr->position()); + instr->CompileToNative(this); } EnsureSpaceForLazyDeopt(); @@ -287,6 +289,10 @@ bool LCodeGen::GenerateDeferredCode() { if (deferred_.length() > 0) { for (int i = 0; !is_aborted() && i < deferred_.length(); i++) { LDeferredCode* code = deferred_[i]; + + int pos = instructions_->at(code->instruction_index())->position(); + RecordAndUpdatePosition(pos); + Comment(";;; <@%d,#%d> " "-------------------- Deferred %s --------------------", code->instruction_index(), @@ -605,37 +611,57 @@ void LCodeGen::WriteTranslation(LEnvironment* environment, break; } + int object_index = 0; + int dematerialized_index = 0; for (int i = 0; i < translation_size; ++i) { LOperand* value = environment->values()->at(i); - - // TODO(mstarzinger): Introduce marker operands to indicate that this value - // is not present and must be reconstructed from the deoptimizer. Currently - // this is only used for the arguments object. - if (value == NULL) { - int arguments_count = environment->values()->length() - translation_size; - translation->BeginArgumentsObject(arguments_count); - for (int i = 0; i < arguments_count; ++i) { - LOperand* value = environment->values()->at(translation_size + i); - AddToTranslation(translation, - value, - environment->HasTaggedValueAt(translation_size + i), - environment->HasUint32ValueAt(translation_size + i)); - } - continue; - } - - AddToTranslation(translation, + AddToTranslation(environment, + translation, value, environment->HasTaggedValueAt(i), - environment->HasUint32ValueAt(i)); + environment->HasUint32ValueAt(i), + &object_index, + &dematerialized_index); } } -void LCodeGen::AddToTranslation(Translation* translation, +void LCodeGen::AddToTranslation(LEnvironment* environment, + Translation* translation, LOperand* op, bool is_tagged, - bool is_uint32) { + bool is_uint32, + int* object_index_pointer, + int* dematerialized_index_pointer) { + if (op == LEnvironment::materialization_marker()) { + int object_index = (*object_index_pointer)++; + if (environment->ObjectIsDuplicateAt(object_index)) { + int dupe_of = environment->ObjectDuplicateOfAt(object_index); + translation->DuplicateObject(dupe_of); + return; + } + int object_length = environment->ObjectLengthAt(object_index); + if (environment->ObjectIsArgumentsAt(object_index)) { + translation->BeginArgumentsObject(object_length); + } else { + translation->BeginCapturedObject(object_length); + } + int dematerialized_index = *dematerialized_index_pointer; + int env_offset = environment->translation_size() + dematerialized_index; + *dematerialized_index_pointer += object_length; + for (int i = 0; i < object_length; ++i) { + LOperand* value = environment->values()->at(env_offset + i); + AddToTranslation(environment, + translation, + value, + environment->HasTaggedValueAt(env_offset + i), + environment->HasUint32ValueAt(env_offset + i), + object_index_pointer, + dematerialized_index_pointer); + } + return; + } + if (op->IsStackSlot()) { if (is_tagged) { translation->StoreStackSlot(op->index()); @@ -762,7 +788,7 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment, } -void LCodeGen::DeoptimizeIf(Condition cc, +void LCodeGen::DeoptimizeIf(Condition condition, LEnvironment* environment, Deoptimizer::BailoutType bailout_type) { RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); @@ -785,12 +811,12 @@ void LCodeGen::DeoptimizeIf(Condition cc, return; } - if (FLAG_trap_on_deopt && info()->IsOptimizing()) { - __ stop("trap_on_deopt", cc); + if (info()->ShouldTrapOnDeopt()) { + __ stop("trap_on_deopt", condition); } ASSERT(info()->IsStub() || frame_is_built_); - if (cc == al && frame_is_built_) { + if (condition == al && frame_is_built_) { __ Call(entry, RelocInfo::RUNTIME_ENTRY); } else { // We often have several deopts to the same entry, reuse the last @@ -804,17 +830,17 @@ void LCodeGen::DeoptimizeIf(Condition cc, !frame_is_built_); deopt_jump_table_.Add(table_entry, zone()); } - __ b(cc, &deopt_jump_table_.last().label); + __ b(condition, &deopt_jump_table_.last().label); } } -void LCodeGen::DeoptimizeIf(Condition cc, +void LCodeGen::DeoptimizeIf(Condition condition, LEnvironment* environment) { Deoptimizer::BailoutType bailout_type = info()->IsStub() ? Deoptimizer::LAZY : Deoptimizer::EAGER; - DeoptimizeIf(cc, environment, bailout_type); + DeoptimizeIf(condition, environment, bailout_type); } @@ -977,6 +1003,14 @@ void LCodeGen::RecordPosition(int position) { } +void LCodeGen::RecordAndUpdatePosition(int position) { + if (position >= 0 && position != old_position_) { + masm()->positions_recorder()->RecordPosition(position); + old_position_ = position; + } +} + + static const char* LabelType(LLabel* label) { if (label->is_loop_header()) return " (loop header)"; if (label->is_osr_entry()) return " (OSR entry)"; @@ -2118,25 +2152,32 @@ int LCodeGen::GetNextEmittedBlock() const { } template -void LCodeGen::EmitBranch(InstrType instr, Condition cc) { +void LCodeGen::EmitBranch(InstrType instr, Condition condition) { int left_block = instr->TrueDestination(chunk_); int right_block = instr->FalseDestination(chunk_); int next_block = GetNextEmittedBlock(); - if (right_block == left_block || cc == al) { + if (right_block == left_block || condition == al) { EmitGoto(left_block); } else if (left_block == next_block) { - __ b(NegateCondition(cc), chunk_->GetAssemblyLabel(right_block)); + __ b(NegateCondition(condition), chunk_->GetAssemblyLabel(right_block)); } else if (right_block == next_block) { - __ b(cc, chunk_->GetAssemblyLabel(left_block)); + __ b(condition, chunk_->GetAssemblyLabel(left_block)); } else { - __ b(cc, chunk_->GetAssemblyLabel(left_block)); + __ b(condition, chunk_->GetAssemblyLabel(left_block)); __ b(chunk_->GetAssemblyLabel(right_block)); } } +template +void LCodeGen::EmitFalseBranch(InstrType instr, Condition condition) { + int false_block = instr->FalseDestination(chunk_); + __ b(condition, chunk_->GetAssemblyLabel(false_block)); +} + + void LCodeGen::DoDebugBreak(LDebugBreak* instr) { __ stop("LBreak"); } @@ -2392,6 +2433,26 @@ void LCodeGen::DoCmpObjectEqAndBranch(LCmpObjectEqAndBranch* instr) { } +void LCodeGen::DoCmpHoleAndBranch(LCmpHoleAndBranch* instr) { + if (instr->hydrogen()->representation().IsTagged()) { + Register input_reg = ToRegister(instr->object()); + __ mov(ip, Operand(factory()->the_hole_value())); + __ cmp(input_reg, ip); + EmitBranch(instr, eq); + return; + } + + DwVfpRegister input_reg = ToDoubleRegister(instr->object()); + __ VFPCompareAndSetFlags(input_reg, input_reg); + EmitFalseBranch(instr, vc); + + Register scratch = scratch0(); + __ VmovHigh(scratch, input_reg); + __ cmp(scratch, Operand(kHoleNanUpper32)); + EmitBranch(instr, eq); +} + + Condition LCodeGen::EmitIsObject(Register input, Register temp1, Label* is_not_object, @@ -3018,91 +3079,6 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) { } -void LCodeGen::EmitLoadFieldOrConstantFunction(Register result, - Register object, - Handle type, - Handle name, - LEnvironment* env) { - LookupResult lookup(isolate()); - type->LookupDescriptor(NULL, *name, &lookup); - ASSERT(lookup.IsFound() || lookup.IsCacheable()); - if (lookup.IsField()) { - int index = lookup.GetLocalFieldIndexFromMap(*type); - int offset = index * kPointerSize; - if (index < 0) { - // Negative property indices are in-object properties, indexed - // from the end of the fixed part of the object. - __ ldr(result, FieldMemOperand(object, offset + type->instance_size())); - } else { - // Non-negative property indices are in the properties array. - __ ldr(result, FieldMemOperand(object, JSObject::kPropertiesOffset)); - __ ldr(result, FieldMemOperand(result, offset + FixedArray::kHeaderSize)); - } - } else if (lookup.IsConstant()) { - Handle constant(lookup.GetConstantFromMap(*type), isolate()); - __ LoadObject(result, constant); - } else { - // Negative lookup. - // Check prototypes. - Handle current(HeapObject::cast((*type)->prototype())); - Heap* heap = type->GetHeap(); - while (*current != heap->null_value()) { - __ LoadHeapObject(result, current); - __ ldr(result, FieldMemOperand(result, HeapObject::kMapOffset)); - __ cmp(result, Operand(Handle(current->map()))); - DeoptimizeIf(ne, env); - current = - Handle(HeapObject::cast(current->map()->prototype())); - } - __ LoadRoot(result, Heap::kUndefinedValueRootIndex); - } -} - - -void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) { - Register object = ToRegister(instr->object()); - Register result = ToRegister(instr->result()); - Register object_map = scratch0(); - - int map_count = instr->hydrogen()->types()->length(); - bool need_generic = instr->hydrogen()->need_generic(); - - if (map_count == 0 && !need_generic) { - DeoptimizeIf(al, instr->environment()); - return; - } - Handle name = instr->hydrogen()->name(); - Label done; - __ ldr(object_map, FieldMemOperand(object, HeapObject::kMapOffset)); - for (int i = 0; i < map_count; ++i) { - bool last = (i == map_count - 1); - Handle map = instr->hydrogen()->types()->at(i); - Label check_passed; - __ CompareMap(object_map, map, &check_passed); - if (last && !need_generic) { - DeoptimizeIf(ne, instr->environment()); - __ bind(&check_passed); - EmitLoadFieldOrConstantFunction( - result, object, map, name, instr->environment()); - } else { - Label next; - __ b(ne, &next); - __ bind(&check_passed); - EmitLoadFieldOrConstantFunction( - result, object, map, name, instr->environment()); - __ b(&done); - __ bind(&next); - } - } - if (need_generic) { - __ mov(r2, Operand(name)); - Handle ic = isolate()->builtins()->LoadIC_Initialize(); - CallCode(ic, RelocInfo::CODE_TARGET, instr, NEVER_INLINE_TARGET_ADDRESS); - } - __ bind(&done); -} - - void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) { ASSERT(ToRegister(instr->object()).is(r0)); ASSERT(ToRegister(instr->result()).is(r0)); @@ -4275,14 +4251,14 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { } -void LCodeGen::ApplyCheckIf(Condition cc, LBoundsCheck* check) { +void LCodeGen::ApplyCheckIf(Condition condition, LBoundsCheck* check) { if (FLAG_debug_code && check->hydrogen()->skip_check()) { Label done; - __ b(NegateCondition(cc), &done); + __ b(NegateCondition(condition), &done); __ stop("eliminated bounds check failed"); __ bind(&done); } else { - DeoptimizeIf(cc, check->environment()); + DeoptimizeIf(condition, check->environment()); } } @@ -4513,12 +4489,13 @@ void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg, scratch, GetLinkRegisterState(), kDontSaveFPRegs); } else { - PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); + PushSafepointRegistersScope scope( + this, Safepoint::kWithRegistersAndDoubles); __ Move(r0, object_reg); __ Move(r1, to_map); TransitionElementsKindStub stub(from_kind, to_kind); __ CallStub(&stub); - RecordSafepointWithRegisters( + RecordSafepointWithRegistersAndDoubles( instr->pointer_map(), 0, Safepoint::kNoLazyDeopt); } __ bind(¬_applicable); @@ -4806,29 +4783,6 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { Register temp1 = ToRegister(instr->temp()); Register temp2 = ToRegister(instr->temp2()); - bool convert_hole = false; - HValue* change_input = instr->hydrogen()->value(); - if (change_input->IsLoadKeyed()) { - HLoadKeyed* load = HLoadKeyed::cast(change_input); - convert_hole = load->UsesMustHandleHole(); - } - - Label no_special_nan_handling; - Label done; - if (convert_hole) { - DwVfpRegister input_reg = ToDoubleRegister(instr->value()); - __ VFPCompareAndSetFlags(input_reg, input_reg); - __ b(vc, &no_special_nan_handling); - __ VmovHigh(scratch, input_reg); - __ cmp(scratch, Operand(kHoleNanUpper32)); - // If not the hole NaN, force the NaN to be canonical. - __ VFPCanonicalizeNaN(input_reg, ne); - __ b(ne, &no_special_nan_handling); - __ Move(reg, factory()->the_hole_value()); - __ b(&done); - } - - __ bind(&no_special_nan_handling); DeferredNumberTagD* deferred = new(zone()) DeferredNumberTagD(this, instr); if (FLAG_inline_new) { __ LoadRoot(scratch, Heap::kHeapNumberMapRootIndex); @@ -4842,7 +4796,6 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { __ vstr(input_reg, reg, HeapNumber::kValueOffset); // Now that we have finished with the object's real address tag it __ add(reg, reg, Operand(kHeapObjectTag)); - __ bind(&done); } @@ -4882,7 +4835,7 @@ void LCodeGen::DoSmiUntag(LSmiUntag* instr) { void LCodeGen::EmitNumberUntagD(Register input_reg, DwVfpRegister result_reg, - bool allow_undefined_as_nan, + bool can_convert_undefined_to_nan, bool deoptimize_on_minus_zero, LEnvironment* env, NumberUntagDMode mode) { @@ -4892,9 +4845,7 @@ void LCodeGen::EmitNumberUntagD(Register input_reg, Label load_smi, heap_number, done; - STATIC_ASSERT(NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE > - NUMBER_CANDIDATE_IS_ANY_TAGGED); - if (mode >= NUMBER_CANDIDATE_IS_ANY_TAGGED) { + if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED) { // Smi check. __ UntagAndJumpIfSmi(scratch, input_reg, &load_smi); @@ -4902,7 +4853,7 @@ void LCodeGen::EmitNumberUntagD(Register input_reg, __ ldr(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset)); __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex); __ cmp(scratch, Operand(ip)); - if (!allow_undefined_as_nan) { + if (!can_convert_undefined_to_nan) { DeoptimizeIf(ne, env); } else { Label heap_number, convert; @@ -4911,11 +4862,6 @@ void LCodeGen::EmitNumberUntagD(Register input_reg, // Convert undefined (and hole) to NaN. __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); __ cmp(input_reg, Operand(ip)); - if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE) { - __ b(eq, &convert); - __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ cmp(input_reg, Operand(ip)); - } DeoptimizeIf(ne, env); __ bind(&convert); @@ -5056,21 +5002,12 @@ void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) { Register input_reg = ToRegister(input); DwVfpRegister result_reg = ToDoubleRegister(result); - NumberUntagDMode mode = NUMBER_CANDIDATE_IS_ANY_TAGGED; HValue* value = instr->hydrogen()->value(); - if (value->type().IsSmi()) { - mode = NUMBER_CANDIDATE_IS_SMI; - } else if (value->IsLoadKeyed()) { - HLoadKeyed* load = HLoadKeyed::cast(value); - if (load->UsesMustHandleHole()) { - if (load->hole_mode() == ALLOW_RETURN_HOLE) { - mode = NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE; - } - } - } + NumberUntagDMode mode = value->representation().IsSmi() + ? NUMBER_CANDIDATE_IS_SMI : NUMBER_CANDIDATE_IS_ANY_TAGGED; EmitNumberUntagD(input_reg, result_reg, - instr->hydrogen()->allow_undefined_as_nan(), + instr->hydrogen()->can_convert_undefined_to_nan(), instr->hydrogen()->deoptimize_on_minus_zero(), instr->environment(), mode); @@ -5200,7 +5137,7 @@ void LCodeGen::DoCheckFunction(LCheckFunction* instr) { AllowDeferredHandleDereference smi_check; if (isolate()->heap()->InNewSpace(*target)) { Register reg = ToRegister(instr->value()); - Handle cell = isolate()->factory()->NewPropertyCell(target); + Handle cell = isolate()->factory()->NewCell(target); __ mov(ip, Operand(Handle(cell))); __ ldr(ip, FieldMemOperand(ip, Cell::kValueOffset)); __ cmp(reg, ip); @@ -5660,6 +5597,8 @@ void LCodeGen::DoDeoptimize(LDeoptimize* instr) { if (info()->IsStub() && type == Deoptimizer::EAGER) { type = Deoptimizer::LAZY; } + + Comment(";;; deoptimize: %s", instr->hydrogen()->reason()); DeoptimizeIf(al, instr->environment(), type); } diff --git a/deps/v8/src/arm/lithium-codegen-arm.h b/deps/v8/src/arm/lithium-codegen-arm.h index 143109c..d0bfcbb 100644 --- a/deps/v8/src/arm/lithium-codegen-arm.h +++ b/deps/v8/src/arm/lithium-codegen-arm.h @@ -66,7 +66,8 @@ class LCodeGen BASE_EMBEDDED { frame_is_built_(false), safepoints_(info->zone()), resolver_(this), - expected_safepoint_kind_(Safepoint::kSimple) { + expected_safepoint_kind_(Safepoint::kSimple), + old_position_(RelocInfo::kNoPosition) { PopulateDeoptimizationLiteralsWithInlinedFunctions(); } @@ -280,16 +281,19 @@ class LCodeGen BASE_EMBEDDED { void RegisterEnvironmentForDeoptimization(LEnvironment* environment, Safepoint::DeoptMode mode); - void DeoptimizeIf(Condition cc, + void DeoptimizeIf(Condition condition, LEnvironment* environment, Deoptimizer::BailoutType bailout_type); - void DeoptimizeIf(Condition cc, LEnvironment* environment); - void ApplyCheckIf(Condition cc, LBoundsCheck* check); + void DeoptimizeIf(Condition condition, LEnvironment* environment); + void ApplyCheckIf(Condition condition, LBoundsCheck* check); - void AddToTranslation(Translation* translation, + void AddToTranslation(LEnvironment* environment, + Translation* translation, LOperand* op, bool is_tagged, - bool is_uint32); + bool is_uint32, + int* object_index_pointer, + int* dematerialized_index_pointer); void RegisterDependentCodeForEmbeddedMaps(Handle code); void PopulateDeoptimizationData(Handle code); int DefineDeoptimizationLiteral(Handle literal); @@ -315,11 +319,14 @@ class LCodeGen BASE_EMBEDDED { int arguments, Safepoint::DeoptMode mode); void RecordPosition(int position); + void RecordAndUpdatePosition(int position); static Condition TokenToCondition(Token::Value op, bool is_unsigned); void EmitGoto(int block); template - void EmitBranch(InstrType instr, Condition cc); + void EmitBranch(InstrType instr, Condition condition); + template + void EmitFalseBranch(InstrType instr, Condition condition); void EmitNumberUntagD(Register input, DwVfpRegister result, bool allow_undefined_as_nan, @@ -355,12 +362,6 @@ class LCodeGen BASE_EMBEDDED { // Caller should branch on equal condition. void EmitIsConstructCall(Register temp1, Register temp2); - void EmitLoadFieldOrConstantFunction(Register result, - Register object, - Handle type, - Handle name, - LEnvironment* env); - // Emits optimized code to deep-copy the contents of statically known // object graphs (e.g. object literal boilerplate). void EmitDeepCopy(Handle object, @@ -417,6 +418,8 @@ class LCodeGen BASE_EMBEDDED { Safepoint::Kind expected_safepoint_kind_; + int old_position_; + class PushSafepointRegistersScope BASE_EMBEDDED { public: PushSafepointRegistersScope(LCodeGen* codegen, diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index b9728ed..a56744b 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -375,16 +375,13 @@ void MacroAssembler::LoadRoot(Register destination, Heap::RootListIndex index, Condition cond) { if (CpuFeatures::IsSupported(MOVW_MOVT_IMMEDIATE_LOADS) && - !Heap::RootCanBeWrittenAfterInitialization(index) && + isolate()->heap()->RootCanBeTreatedAsConstant(index) && !predictable_code_size()) { - Handle root(isolate()->heap()->roots_array_start()[index], - isolate()); - if (!isolate()->heap()->InNewSpace(*root)) { - // The CPU supports fast immediate values, and this root will never - // change. We will load it as a relocatable immediate value. - mov(destination, Operand(root), LeaveCC, cond); - return; - } + // The CPU supports fast immediate values, and this root will never + // change. We will load it as a relocatable immediate value. + Handle root(&isolate()->heap()->roots_array_start()[index]); + mov(destination, Operand(root), LeaveCC, cond); + return; } ldr(destination, MemOperand(kRootRegister, index << kPointerSizeLog2), cond); } diff --git a/deps/v8/src/assert-scope.h b/deps/v8/src/assert-scope.h index 13adbd0..269b280 100644 --- a/deps/v8/src/assert-scope.h +++ b/deps/v8/src/assert-scope.h @@ -41,6 +41,7 @@ enum PerThreadAssertType { HANDLE_ALLOCATION_ASSERT, HANDLE_DEREFERENCE_ASSERT, DEFERRED_HANDLE_DEREFERENCE_ASSERT, + CODE_DEPENDENCY_CHANGE_ASSERT, LAST_PER_THREAD_ASSERT_TYPE }; @@ -170,6 +171,14 @@ typedef PerThreadAssertScope typedef PerThreadAssertScope AllowDeferredHandleDereference; +// Scope to document where we do not expect deferred handles to be dereferenced. +typedef PerThreadAssertScope + DisallowCodeDependencyChange; + +// Scope to introduce an exception to DisallowDeferredHandleDereference. +typedef PerThreadAssertScope + AllowCodeDependencyChange; + } } // namespace v8::internal #endif // V8_ASSERT_SCOPE_H_ diff --git a/deps/v8/src/ast.cc b/deps/v8/src/ast.cc index 2077f87..23b680d 100644 --- a/deps/v8/src/ast.cc +++ b/deps/v8/src/ast.cc @@ -273,7 +273,8 @@ void ObjectLiteral::CalculateEmitStore(Zone* zone) { uint32_t hash = literal->Hash(); // If the key of a computed property is in the table, do not emit // a store for the property later. - if (property->kind() == ObjectLiteral::Property::COMPUTED && + if ((property->kind() == ObjectLiteral::Property::MATERIALIZED_LITERAL || + property->kind() == ObjectLiteral::Property::COMPUTED) && table.Lookup(literal, hash, false, allocator) != NULL) { property->set_emit_store(false); } else { diff --git a/deps/v8/src/ast.h b/deps/v8/src/ast.h index 0812472..a8b7421 100644 --- a/deps/v8/src/ast.h +++ b/deps/v8/src/ast.h @@ -259,6 +259,7 @@ class Statement: public AstNode { Statement() : statement_pos_(RelocInfo::kNoPosition) {} bool IsEmpty() { return AsEmptyStatement() != NULL; } + virtual bool IsJump() const { return false; } void set_statement_pos(int statement_pos) { statement_pos_ = statement_pos; } int statement_pos() const { return statement_pos_; } @@ -388,7 +389,7 @@ class Expression: public AstNode { protected: explicit Expression(Isolate* isolate) - : bounds_(Type::None(), Type::Any(), isolate), + : bounds_(Bounds::Unbounded(isolate)), id_(GetNextId(isolate)), test_id_(GetNextId(isolate)) {} void set_to_boolean_types(byte types) { to_boolean_types_ = types; } @@ -458,6 +459,11 @@ class Block: public BreakableStatement { ZoneList* statements() { return &statements_; } bool is_initializer_block() const { return is_initializer_block_; } + virtual bool IsJump() const { + return !statements_.is_empty() && statements_.last()->IsJump() + && labels() == NULL; // Good enough as an approximation... + } + Scope* scope() const { return scope_; } void set_scope(Scope* scope) { scope_ = scope; } @@ -1008,6 +1014,7 @@ class ExpressionStatement: public Statement { void set_expression(Expression* e) { expression_ = e; } Expression* expression() const { return expression_; } + virtual bool IsJump() const { return expression_->IsThrow(); } protected: explicit ExpressionStatement(Expression* expression) @@ -1018,7 +1025,16 @@ class ExpressionStatement: public Statement { }; -class ContinueStatement: public Statement { +class JumpStatement: public Statement { + public: + virtual bool IsJump() const { return true; } + + protected: + JumpStatement() {} +}; + + +class ContinueStatement: public JumpStatement { public: DECLARE_NODE_TYPE(ContinueStatement) @@ -1033,7 +1049,7 @@ class ContinueStatement: public Statement { }; -class BreakStatement: public Statement { +class BreakStatement: public JumpStatement { public: DECLARE_NODE_TYPE(BreakStatement) @@ -1048,7 +1064,7 @@ class BreakStatement: public Statement { }; -class ReturnStatement: public Statement { +class ReturnStatement: public JumpStatement { public: DECLARE_NODE_TYPE(ReturnStatement) @@ -1167,6 +1183,11 @@ class IfStatement: public Statement { Statement* then_statement() const { return then_statement_; } Statement* else_statement() const { return else_statement_; } + virtual bool IsJump() const { + return HasThenStatement() && then_statement()->IsJump() + && HasElseStatement() && else_statement()->IsJump(); + } + BailoutId IfId() const { return if_id_; } BailoutId ThenId() const { return then_id_; } BailoutId ElseId() const { return else_id_; } diff --git a/deps/v8/src/code-stubs-hydrogen.cc b/deps/v8/src/code-stubs-hydrogen.cc index 852f7b5..4f6db35 100644 --- a/deps/v8/src/code-stubs-hydrogen.cc +++ b/deps/v8/src/code-stubs-hydrogen.cc @@ -92,7 +92,7 @@ class CodeStubGraphBuilderBase : public HGraphBuilder { } ~ArrayContextChecker() { - checker_.ElseDeopt(); + checker_.ElseDeopt("Array constructor called from different context"); checker_.End(); } private: @@ -233,7 +233,7 @@ class CodeStubGraphBuilder: public CodeStubGraphBuilderBase { IfBuilder builder(this); builder.IfNot(undefined, undefined); builder.Then(); - builder.ElseDeopt(); + builder.ElseDeopt("Forced deopt to runtime"); return undefined; } @@ -352,7 +352,7 @@ HValue* CodeStubGraphBuilder::BuildCodeStub() { HObjectAccess access = HObjectAccess::ForAllocationSiteTransitionInfo(); HInstruction* boilerplate = Add(allocation_site, access); if (mode == FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS) { - HValue* elements = AddLoadElements(boilerplate); + HValue* elements = AddLoadElements(boilerplate, NULL); IfBuilder if_fixed_cow(this); if_fixed_cow.If(elements, factory->fixed_cow_array_map()); @@ -387,7 +387,7 @@ HValue* CodeStubGraphBuilder::BuildCodeStub() { length)); } - checker.ElseDeopt(); + checker.ElseDeopt("Uninitialized boilerplate literals"); checker.End(); return environment()->Pop(); @@ -434,7 +434,7 @@ HValue* CodeStubGraphBuilder::BuildCodeStub() { } environment()->Push(object); - checker.ElseDeopt(); + checker.ElseDeopt("Uninitialized boilerplate in fast clone"); checker.End(); return environment()->Pop(); @@ -513,7 +513,7 @@ HValue* CodeStubGraphBuilder::BuildCodeStub() { HObjectAccess access = casted_stub()->is_inobject() ? HObjectAccess::ForJSObjectOffset(casted_stub()->offset(), rep) : HObjectAccess::ForBackingStoreOffset(casted_stub()->offset(), rep); - return AddInstruction(BuildLoadNamedField(GetParameter(0), access)); + return AddInstruction(BuildLoadNamedField(GetParameter(0), access, NULL)); } @@ -528,7 +528,7 @@ HValue* CodeStubGraphBuilder::BuildCodeStub() { HObjectAccess access = casted_stub()->is_inobject() ? HObjectAccess::ForJSObjectOffset(casted_stub()->offset(), rep) : HObjectAccess::ForBackingStoreOffset(casted_stub()->offset(), rep); - return AddInstruction(BuildLoadNamedField(GetParameter(0), access)); + return AddInstruction(BuildLoadNamedField(GetParameter(0), access, NULL)); } @@ -844,7 +844,7 @@ HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { IfBuilder builder(this); builder.If(cell_contents, value); builder.Then(); - builder.ElseDeopt(); + builder.ElseDeopt("Unexpected cell contents in constant global store"); builder.End(); } else { // Load the payload of the global parameter cell. A hole indicates that the @@ -854,7 +854,7 @@ HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { HValue* hole_value = Add(hole); builder.If(cell_contents, hole_value); builder.Then(); - builder.Deopt(); + builder.Deopt("Unexpected cell contents in global store"); builder.Else(); Add(cell, access, value); builder.End(); @@ -878,7 +878,8 @@ HValue* CodeStubGraphBuilder::BuildCodeStub() { if (FLAG_trace_elements_transitions) { // Tracing elements transitions is the job of the runtime. - Add(Deoptimizer::EAGER); + Add("Deopt due to --trace-elements-transitions", + Deoptimizer::EAGER); } else { info()->MarkAsSavesCallerDoubles(); diff --git a/deps/v8/src/code-stubs.cc b/deps/v8/src/code-stubs.cc index d472fa2..f656bf7 100644 --- a/deps/v8/src/code-stubs.cc +++ b/deps/v8/src/code-stubs.cc @@ -469,6 +469,9 @@ void CompareNilICStub::UpdateStatus(Handle object) { template void HydrogenCodeStub::TraceTransition(StateType from, StateType to) { + // Note: Although a no-op transition is semantically OK, it is hinting at a + // bug somewhere in our state transition machinery. + ASSERT(from != to); #ifdef DEBUG if (!FLAG_trace_ic) return; char buffer[100]; diff --git a/deps/v8/src/compiler.cc b/deps/v8/src/compiler.cc index ebd4995..f6e5daa 100644 --- a/deps/v8/src/compiler.cc +++ b/deps/v8/src/compiler.cc @@ -120,6 +120,7 @@ void CompilationInfo::Initialize(Isolate* isolate, return; } mode_ = V8::UseCrankshaft() ? mode : NONOPT; + abort_due_to_dependency_ = false; if (script_->type()->value() == Script::TYPE_NATIVE) { MarkAsNative(); } @@ -446,6 +447,12 @@ OptimizingCompiler::Status OptimizingCompiler::CreateGraph() { } } + if (info()->HasAbortedDueToDependencyChange()) { + info_->set_bailout_reason(kBailedOutDueToDependencyChange); + info_->AbortOptimization(); + return SetLastStatus(BAILED_OUT); + } + return SetLastStatus(SUCCEEDED); } @@ -454,6 +461,7 @@ OptimizingCompiler::Status OptimizingCompiler::OptimizeGraph() { DisallowHeapAllocation no_allocation; DisallowHandleAllocation no_handles; DisallowHandleDereference no_deref; + DisallowCodeDependencyChange no_dependency_change; ASSERT(last_status() == SUCCEEDED); Timer t(this, &time_taken_to_optimize_); @@ -474,6 +482,8 @@ OptimizingCompiler::Status OptimizingCompiler::OptimizeGraph() { OptimizingCompiler::Status OptimizingCompiler::GenerateAndInstallCode() { ASSERT(last_status() == SUCCEEDED); + ASSERT(!info()->HasAbortedDueToDependencyChange()); + DisallowCodeDependencyChange no_dependency_change; { // Scope for timer. Timer timer(this, &time_taken_to_codegen_); ASSERT(chunk_ != NULL); @@ -485,7 +495,7 @@ OptimizingCompiler::Status OptimizingCompiler::GenerateAndInstallCode() { DisallowDeferredHandleDereference no_deferred_handle_deref; Handle optimized_code = chunk_->Codegen(); if (optimized_code.is_null()) { - if (info()->bailout_reason() != kNoReason) { + if (info()->bailout_reason() == kNoReason) { info()->set_bailout_reason(kCodeGenerationFailed); } return AbortOptimization(); @@ -815,6 +825,7 @@ static bool InstallFullCode(CompilationInfo* info) { // was flushed. By setting the code object last we avoid this. Handle shared = info->shared_info(); Handle code = info->code(); + CHECK(code->kind() == Code::FUNCTION); Handle function = info->closure(); Handle scope_info = ScopeInfo::Create(info->scope(), info->zone()); @@ -972,7 +983,9 @@ void Compiler::RecompileParallel(Handle closure) { if (!isolate->optimizing_compiler_thread()->IsQueueAvailable()) { if (FLAG_trace_parallel_recompilation) { - PrintF(" ** Compilation queue, will retry opting on next run.\n"); + PrintF(" ** Compilation queue full, will retry optimizing "); + closure->PrintName(); + PrintF(" on next run.\n"); } return; } @@ -1057,7 +1070,7 @@ void Compiler::InstallOptimizedCode(OptimizingCompiler* optimizing_compiler) { // the unoptimized code. OptimizingCompiler::Status status = optimizing_compiler->last_status(); if (info->HasAbortedDueToDependencyChange()) { - info->set_bailout_reason(kBailedOutDueToDependentMap); + info->set_bailout_reason(kBailedOutDueToDependencyChange); status = optimizing_compiler->AbortOptimization(); } else if (status != OptimizingCompiler::SUCCEEDED) { info->set_bailout_reason(kFailedBailedOutLastTime); diff --git a/deps/v8/src/compiler.h b/deps/v8/src/compiler.h index 50053e5..7d442f9 100644 --- a/deps/v8/src/compiler.h +++ b/deps/v8/src/compiler.h @@ -199,6 +199,11 @@ class CompilationInfo { return IsCompilingForDebugging::decode(flags_); } + bool ShouldTrapOnDeopt() const { + return (FLAG_trap_on_deopt && IsOptimizing()) || + (FLAG_trap_on_stub_deopt && IsStub()); + } + bool has_global_object() const { return !closure().is_null() && (closure()->context()->global_object() != NULL); @@ -293,11 +298,13 @@ class CompilationInfo { } void AbortDueToDependencyChange() { - mode_ = DEPENDENCY_CHANGE_ABORT; + ASSERT(!isolate()->optimizing_compiler_thread()->IsOptimizerThread()); + abort_due_to_dependency_ = true; } bool HasAbortedDueToDependencyChange() { - return mode_ == DEPENDENCY_CHANGE_ABORT; + ASSERT(!isolate()->optimizing_compiler_thread()->IsOptimizerThread()); + return abort_due_to_dependency_; } protected: @@ -321,8 +328,7 @@ class CompilationInfo { BASE, OPTIMIZE, NONOPT, - STUB, - DEPENDENCY_CHANGE_ABORT + STUB }; void Initialize(Isolate* isolate, Mode mode, Zone* zone); @@ -396,6 +402,9 @@ class CompilationInfo { Mode mode_; BailoutId osr_ast_id_; + // Flag whether compilation needs to be aborted due to dependency change. + bool abort_due_to_dependency_; + // The zone from which the compilation pipeline working on this // CompilationInfo allocates. Zone* zone_; diff --git a/deps/v8/src/cpu-profiler.cc b/deps/v8/src/cpu-profiler.cc index 0a83b85..747542f 100644 --- a/deps/v8/src/cpu-profiler.cc +++ b/deps/v8/src/cpu-profiler.cc @@ -106,7 +106,7 @@ bool ProfilerEventsProcessor::ProcessCodeEvent() { bool ProfilerEventsProcessor::ProcessTicks() { while (true) { - if (!ticks_from_vm_buffer_.IsEmpty() + while (!ticks_from_vm_buffer_.IsEmpty() && ticks_from_vm_buffer_.Peek()->order == last_processed_code_event_id_) { TickSampleEventRecord record; diff --git a/deps/v8/src/cpu-profiler.h b/deps/v8/src/cpu-profiler.h index cbe3e3c..1dd405e 100644 --- a/deps/v8/src/cpu-profiler.h +++ b/deps/v8/src/cpu-profiler.h @@ -241,6 +241,7 @@ class CpuProfiler : public CodeEventListener { ProfileGenerator* generator() const { return generator_; } ProfilerEventsProcessor* processor() const { return processor_; } + Isolate* isolate() const { return isolate_; } private: void StartProcessorIfNotStarted(); @@ -258,7 +259,6 @@ class CpuProfiler : public CodeEventListener { bool need_to_stop_sampler_; bool is_profiling_; - private: DISALLOW_COPY_AND_ASSIGN(CpuProfiler); }; diff --git a/deps/v8/src/debug.cc b/deps/v8/src/debug.cc index a349502..4966713 100644 --- a/deps/v8/src/debug.cc +++ b/deps/v8/src/debug.cc @@ -409,6 +409,9 @@ bool BreakLocationIterator::IsStepInLocation(Isolate* isolate) { HandleScope scope(debug_info_->GetIsolate()); Address target = rinfo()->target_address(); Handle target_code(Code::GetCodeFromTargetAddress(target)); + if (target_code->kind() == Code::STUB) { + return target_code->major_key() == CodeStub::CallFunction; + } return target_code->is_call_stub() || target_code->is_keyed_call_stub(); } else { return false; @@ -2044,6 +2047,10 @@ void Debug::PrepareForBreakPoints() { // If preparing for the first break point make sure to deoptimize all // functions as debugging does not work with optimized code. if (!has_break_points_) { + if (FLAG_parallel_recompilation) { + isolate_->optimizing_compiler_thread()->Flush(); + } + Deoptimizer::DeoptimizeAll(isolate_); Handle lazy_compile = diff --git a/deps/v8/src/deoptimizer.cc b/deps/v8/src/deoptimizer.cc index 50d6f0b..dc9ffc5 100644 --- a/deps/v8/src/deoptimizer.cc +++ b/deps/v8/src/deoptimizer.cc @@ -602,6 +602,12 @@ Deoptimizer::Deoptimizer(Isolate* isolate, deferred_objects_double_values_(0), deferred_objects_(0), deferred_heap_numbers_(0), + jsframe_functions_(0), + jsframe_has_adapted_arguments_(0), + materialized_values_(NULL), + materialized_objects_(NULL), + materialization_value_index_(0), + materialization_object_index_(0), trace_(false) { // For COMPILED_STUBs called from builtins, the function pointer is a SMI // indicating an internal frame. @@ -1208,7 +1214,15 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslationIterator* iterator, unsigned output_offset = output_frame_size; for (int i = 0; i < parameter_count; ++i) { output_offset -= kPointerSize; + int deferred_object_index = deferred_objects_.length(); DoTranslateCommand(iterator, frame_index, output_offset); + // The allocated receiver of a construct stub frame is passed as the + // receiver parameter through the translation. It might be encoding + // a captured object, patch the slot address for a captured object. + if (i == 0 && deferred_objects_.length() > deferred_object_index) { + ASSERT(!deferred_objects_[deferred_object_index].is_arguments()); + deferred_objects_[deferred_object_index].patch_slot_address(top_address); + } } // Read caller's PC from the previous frame. @@ -1633,9 +1647,93 @@ void Deoptimizer::DoComputeCompiledStubFrame(TranslationIterator* iterator, } +Handle Deoptimizer::MaterializeNextHeapObject() { + int object_index = materialization_object_index_++; + ObjectMaterializationDescriptor desc = deferred_objects_[object_index]; + const int length = desc.object_length(); + + if (desc.duplicate_object() >= 0) { + // Found a previously materialized object by de-duplication. + object_index = desc.duplicate_object(); + materialized_objects_->Add(Handle()); + } else if (desc.is_arguments() && ArgumentsObjectIsAdapted(object_index)) { + // Use the arguments adapter frame we just built to materialize the + // arguments object. FunctionGetArguments can't throw an exception. + Handle function = ArgumentsObjectFunction(object_index); + Handle arguments = Handle::cast( + Accessors::FunctionGetArguments(function)); + materialized_objects_->Add(arguments); + materialization_value_index_ += length; + } else if (desc.is_arguments()) { + // Construct an arguments object and copy the parameters to a newly + // allocated arguments object backing store. + Handle function = ArgumentsObjectFunction(object_index); + Handle arguments = + isolate_->factory()->NewArgumentsObject(function, length); + Handle array = isolate_->factory()->NewFixedArray(length); + ASSERT(array->length() == length); + arguments->set_elements(*array); + materialized_objects_->Add(arguments); + for (int i = 0; i < length; ++i) { + Handle value = MaterializeNextValue(); + array->set(i, *value); + } + } else { + // Dispatch on the instance type of the object to be materialized. + Handle map = Handle::cast(MaterializeNextValue()); + switch (map->instance_type()) { + case HEAP_NUMBER_TYPE: { + Handle number = + Handle::cast(MaterializeNextValue()); + materialized_objects_->Add(number); + materialization_value_index_ += kDoubleSize / kPointerSize - 1; + break; + } + case JS_OBJECT_TYPE: { + Handle object = + isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED, false); + materialized_objects_->Add(object); + Handle properties = MaterializeNextValue(); + Handle elements = MaterializeNextValue(); + object->set_properties(FixedArray::cast(*properties)); + object->set_elements(FixedArray::cast(*elements)); + for (int i = 0; i < length - 3; ++i) { + Handle value = MaterializeNextValue(); + object->FastPropertyAtPut(i, *value); + } + break; + } + default: + PrintF("[couldn't handle instance type %d]\n", map->instance_type()); + UNREACHABLE(); + } + } + + return materialized_objects_->at(object_index); +} + + +Handle Deoptimizer::MaterializeNextValue() { + int value_index = materialization_value_index_++; + Handle value = materialized_values_->at(value_index); + if (*value == isolate_->heap()->arguments_marker()) { + value = MaterializeNextHeapObject(); + } + return value; +} + + void Deoptimizer::MaterializeHeapObjects(JavaScriptFrameIterator* it) { ASSERT_NE(DEBUGGER, bailout_type_); + // Walk all JavaScript output frames with the given frame iterator. + for (int frame_index = 0; frame_index < jsframe_count(); ++frame_index) { + if (frame_index != 0) it->Advance(); + JavaScriptFrame* frame = it->frame(); + jsframe_functions_.Add(handle(frame->function(), isolate_)); + jsframe_has_adapted_arguments_.Add(frame->has_adapted_arguments()); + } + // Handlify all tagged object values before triggering any allocation. List > values(deferred_objects_tagged_values_.length()); for (int i = 0; i < deferred_objects_tagged_values_.length(); ++i) { @@ -1652,7 +1750,7 @@ void Deoptimizer::MaterializeHeapObjects(JavaScriptFrameIterator* it) { HeapNumberMaterializationDescriptor d = deferred_heap_numbers_[i]; Handle num = isolate_->factory()->NewNumber(d.value()); if (trace_) { - PrintF("Materializing a new heap number %p [%e] in slot %p\n", + PrintF("Materialized a new heap number %p [%e] in slot %p\n", reinterpret_cast(*num), d.value(), d.slot_address()); @@ -1660,62 +1758,52 @@ void Deoptimizer::MaterializeHeapObjects(JavaScriptFrameIterator* it) { Memory::Object_at(d.slot_address()) = *num; } - // Materialize all heap numbers required for arguments objects. + // Materialize all heap numbers required for arguments/captured objects. for (int i = 0; i < values.length(); i++) { if (!values.at(i)->IsTheHole()) continue; double double_value = deferred_objects_double_values_[i]; Handle num = isolate_->factory()->NewNumber(double_value); if (trace_) { - PrintF("Materializing a new heap number %p [%e] for arguments object\n", + PrintF("Materialized a new heap number %p [%e] for object\n", reinterpret_cast(*num), double_value); } values.Set(i, num); } - // Materialize arguments objects one frame at a time. - for (int frame_index = 0; frame_index < jsframe_count(); ++frame_index) { - if (frame_index != 0) it->Advance(); - JavaScriptFrame* frame = it->frame(); - Handle function(frame->function(), isolate_); - Handle arguments; - for (int i = frame->ComputeExpressionsCount() - 1; i >= 0; --i) { - if (frame->GetExpression(i) == isolate_->heap()->arguments_marker()) { - ObjectMaterializationDescriptor descriptor = - deferred_objects_.RemoveLast(); - const int length = descriptor.object_length(); - if (arguments.is_null()) { - if (frame->has_adapted_arguments()) { - // Use the arguments adapter frame we just built to materialize the - // arguments object. FunctionGetArguments can't throw an exception. - arguments = Handle::cast( - Accessors::FunctionGetArguments(function)); - values.RewindBy(length); - } else { - // Construct an arguments object and copy the parameters to a newly - // allocated arguments object backing store. - arguments = - isolate_->factory()->NewArgumentsObject(function, length); - Handle array = - isolate_->factory()->NewFixedArray(length); - ASSERT(array->length() == length); - for (int i = length - 1; i >= 0 ; --i) { - array->set(i, *values.RemoveLast()); - } - arguments->set_elements(*array); - } - } - frame->SetExpression(i, *arguments); - ASSERT_EQ(Memory::Object_at(descriptor.slot_address()), *arguments); - if (trace_) { - PrintF("Materializing %sarguments object of length %d for %p: ", - frame->has_adapted_arguments() ? "(adapted) " : "", - arguments->elements()->length(), + // Materialize arguments/captured objects. + if (!deferred_objects_.is_empty()) { + List > materialized_objects(deferred_objects_.length()); + materialized_objects_ = &materialized_objects; + materialized_values_ = &values; + + while (materialization_object_index_ < deferred_objects_.length()) { + int object_index = materialization_object_index_; + ObjectMaterializationDescriptor descriptor = + deferred_objects_.at(object_index); + + // Find a previously materialized object by de-duplication or + // materialize a new instance of the object if necessary. Store + // the materialized object into the frame slot. + Handle object = MaterializeNextHeapObject(); + Memory::Object_at(descriptor.slot_address()) = *object; + if (trace_) { + if (descriptor.is_arguments()) { + PrintF("Materialized %sarguments object of length %d for %p: ", + ArgumentsObjectIsAdapted(object_index) ? "(adapted) " : "", + Handle::cast(object)->elements()->length(), + reinterpret_cast(descriptor.slot_address())); + } else { + PrintF("Materialized captured object of size %d for %p: ", + Handle::cast(object)->Size(), reinterpret_cast(descriptor.slot_address())); - arguments->ShortPrint(); - PrintF("\n"); } + object->ShortPrint(); + PrintF("\n"); } } + + ASSERT(materialization_object_index_ == materialized_objects_->length()); + ASSERT(materialization_value_index_ == materialized_values_->length()); } } @@ -1786,10 +1874,10 @@ static const char* TraceValueType(bool is_smi, bool is_native = false) { void Deoptimizer::DoTranslateObject(TranslationIterator* iterator, - int object_opcode, + int object_index, int field_index) { disasm::NameConverter converter; - Address object_slot = deferred_objects_.last().slot_address(); + Address object_slot = deferred_objects_[object_index].slot_address(); Translation::Opcode opcode = static_cast(iterator->Next()); @@ -1802,7 +1890,6 @@ void Deoptimizer::DoTranslateObject(TranslationIterator* iterator, case Translation::GETTER_STUB_FRAME: case Translation::SETTER_STUB_FRAME: case Translation::COMPILED_STUB_FRAME: - case Translation::ARGUMENTS_OBJECT: UNREACHABLE(); return; @@ -1972,6 +2059,50 @@ void Deoptimizer::DoTranslateObject(TranslationIterator* iterator, AddObjectTaggedValue(value); return; } + + case Translation::DUPLICATED_OBJECT: { + int object_index = iterator->Next(); + if (trace_) { + PrintF(" nested @0x%08" V8PRIxPTR ": [field #%d] <- ", + reinterpret_cast(object_slot), + field_index); + isolate_->heap()->arguments_marker()->ShortPrint(); + PrintF(" ; duplicate of object #%d\n", object_index); + } + // Use the materialization marker value as a sentinel and fill in + // the object after the deoptimized frame is built. + intptr_t value = reinterpret_cast( + isolate_->heap()->arguments_marker()); + AddObjectDuplication(0, object_index); + AddObjectTaggedValue(value); + return; + } + + case Translation::ARGUMENTS_OBJECT: + case Translation::CAPTURED_OBJECT: { + int length = iterator->Next(); + bool is_args = opcode == Translation::ARGUMENTS_OBJECT; + if (trace_) { + PrintF(" nested @0x%08" V8PRIxPTR ": [field #%d] <- ", + reinterpret_cast(object_slot), + field_index); + isolate_->heap()->arguments_marker()->ShortPrint(); + PrintF(" ; object (length = %d, is_args = %d)\n", length, is_args); + } + // Use the materialization marker value as a sentinel and fill in + // the object after the deoptimized frame is built. + intptr_t value = reinterpret_cast( + isolate_->heap()->arguments_marker()); + AddObjectStart(0, length, is_args); + AddObjectTaggedValue(value); + // We save the object values on the side and materialize the actual + // object after the deoptimized frame is built. + int object_index = deferred_objects_.length() - 1; + for (int i = 0; i < length; i++) { + DoTranslateObject(iterator, object_index, i); + } + return; + } } } @@ -2211,25 +2342,48 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, return; } - case Translation::ARGUMENTS_OBJECT: { + case Translation::DUPLICATED_OBJECT: { + int object_index = iterator->Next(); + if (trace_) { + PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- ", + output_[frame_index]->GetTop() + output_offset, + output_offset); + isolate_->heap()->arguments_marker()->ShortPrint(); + PrintF(" ; duplicate of object #%d\n", object_index); + } + // Use the materialization marker value as a sentinel and fill in + // the object after the deoptimized frame is built. + intptr_t value = reinterpret_cast( + isolate_->heap()->arguments_marker()); + AddObjectDuplication(output_[frame_index]->GetTop() + output_offset, + object_index); + output_[frame_index]->SetFrameSlot(output_offset, value); + return; + } + + case Translation::ARGUMENTS_OBJECT: + case Translation::CAPTURED_OBJECT: { int length = iterator->Next(); + bool is_args = opcode == Translation::ARGUMENTS_OBJECT; if (trace_) { PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- ", output_[frame_index]->GetTop() + output_offset, output_offset); isolate_->heap()->arguments_marker()->ShortPrint(); - PrintF(" ; arguments object (length = %d)\n", length); + PrintF(" ; object (length = %d, is_args = %d)\n", length, is_args); } - // Use the arguments marker value as a sentinel and fill in the arguments - // object after the deoptimized frame is built. + // Use the materialization marker value as a sentinel and fill in + // the object after the deoptimized frame is built. intptr_t value = reinterpret_cast( isolate_->heap()->arguments_marker()); - AddObjectStart(output_[frame_index]->GetTop() + output_offset, length); + AddObjectStart(output_[frame_index]->GetTop() + output_offset, + length, is_args); output_[frame_index]->SetFrameSlot(output_offset, value); - // We save the argument values on the side and materialize the actual - // arguments object after the deoptimized frame is built. + // We save the object values on the side and materialize the actual + // object after the deoptimized frame is built. + int object_index = deferred_objects_.length() - 1; for (int i = 0; i < length; i++) { - DoTranslateObject(iterator, Translation::ARGUMENTS_OBJECT, i); + DoTranslateObject(iterator, object_index, i); } return; } @@ -2406,7 +2560,9 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator, break; } - case Translation::ARGUMENTS_OBJECT: { + case Translation::DUPLICATED_OBJECT: + case Translation::ARGUMENTS_OBJECT: + case Translation::CAPTURED_OBJECT: { // Optimized code assumes that the argument object has not been // materialized and so bypasses it when doing arguments access. // We should have bailed out before starting the frame @@ -2554,9 +2710,16 @@ Object* Deoptimizer::ComputeLiteral(int index) const { } -void Deoptimizer::AddObjectStart(intptr_t slot_address, int length) { +void Deoptimizer::AddObjectStart(intptr_t slot, int length, bool is_args) { ObjectMaterializationDescriptor object_desc( - reinterpret_cast
(slot_address), length); + reinterpret_cast
(slot), jsframe_count_, length, -1, is_args); + deferred_objects_.Add(object_desc); +} + + +void Deoptimizer::AddObjectDuplication(intptr_t slot, int object_index) { + ObjectMaterializationDescriptor object_desc( + reinterpret_cast
(slot), jsframe_count_, -1, object_index, false); deferred_objects_.Add(object_desc); } @@ -2784,6 +2947,18 @@ void Translation::BeginArgumentsObject(int args_length) { } +void Translation::BeginCapturedObject(int length) { + buffer_->Add(CAPTURED_OBJECT, zone()); + buffer_->Add(length, zone()); +} + + +void Translation::DuplicateObject(int object_index) { + buffer_->Add(DUPLICATED_OBJECT, zone()); + buffer_->Add(object_index, zone()); +} + + void Translation::StoreRegister(Register reg) { buffer_->Add(REGISTER, zone()); buffer_->Add(reg.code(), zone()); @@ -2852,7 +3027,9 @@ int Translation::NumberOfOperandsFor(Opcode opcode) { switch (opcode) { case GETTER_STUB_FRAME: case SETTER_STUB_FRAME: + case DUPLICATED_OBJECT: case ARGUMENTS_OBJECT: + case CAPTURED_OBJECT: case REGISTER: case INT32_REGISTER: case UINT32_REGISTER: @@ -2912,8 +3089,12 @@ const char* Translation::StringFor(Opcode opcode) { return "DOUBLE_STACK_SLOT"; case LITERAL: return "LITERAL"; + case DUPLICATED_OBJECT: + return "DUPLICATED_OBJECT"; case ARGUMENTS_OBJECT: return "ARGUMENTS_OBJECT"; + case CAPTURED_OBJECT: + return "CAPTURED_OBJECT"; } UNREACHABLE(); return ""; @@ -2957,7 +3138,9 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator, // Peeled off before getting here. break; + case Translation::DUPLICATED_OBJECT: case Translation::ARGUMENTS_OBJECT: + case Translation::CAPTURED_OBJECT: // This can be only emitted for local slots not for argument slots. break; diff --git a/deps/v8/src/deoptimizer.h b/deps/v8/src/deoptimizer.h index 7ad1ab0..b6e4667 100644 --- a/deps/v8/src/deoptimizer.h +++ b/deps/v8/src/deoptimizer.h @@ -77,15 +77,31 @@ class HeapNumberMaterializationDescriptor BASE_EMBEDDED { class ObjectMaterializationDescriptor BASE_EMBEDDED { public: - ObjectMaterializationDescriptor(Address slot_address, int length) - : slot_address_(slot_address), object_length_(length) { } + ObjectMaterializationDescriptor( + Address slot_address, int frame, int length, int duplicate, bool is_args) + : slot_address_(slot_address), + jsframe_index_(frame), + object_length_(length), + duplicate_object_(duplicate), + is_arguments_(is_args) { } Address slot_address() const { return slot_address_; } + int jsframe_index() const { return jsframe_index_; } int object_length() const { return object_length_; } + int duplicate_object() const { return duplicate_object_; } + bool is_arguments() const { return is_arguments_; } + + // Only used for allocated receivers in DoComputeConstructStubFrame. + void patch_slot_address(intptr_t slot) { + slot_address_ = reinterpret_cast
(slot); + } private: Address slot_address_; + int jsframe_index_; int object_length_; + int duplicate_object_; + bool is_arguments_; }; @@ -372,7 +388,7 @@ class Deoptimizer : public Malloced { int frame_index); void DoTranslateObject(TranslationIterator* iterator, - int object_opcode, + int object_index, int field_index); enum DeoptimizerTranslatedValueType { @@ -400,11 +416,28 @@ class Deoptimizer : public Malloced { Object* ComputeLiteral(int index) const; - void AddObjectStart(intptr_t slot_address, int argc); + void AddObjectStart(intptr_t slot_address, int argc, bool is_arguments); + void AddObjectDuplication(intptr_t slot, int object_index); void AddObjectTaggedValue(intptr_t value); void AddObjectDoubleValue(double value); void AddDoubleValue(intptr_t slot_address, double value); + bool ArgumentsObjectIsAdapted(int object_index) { + ObjectMaterializationDescriptor desc = deferred_objects_.at(object_index); + int reverse_jsframe_index = jsframe_count_ - desc.jsframe_index() - 1; + return jsframe_has_adapted_arguments_[reverse_jsframe_index]; + } + + Handle ArgumentsObjectFunction(int object_index) { + ObjectMaterializationDescriptor desc = deferred_objects_.at(object_index); + int reverse_jsframe_index = jsframe_count_ - desc.jsframe_index() - 1; + return jsframe_functions_[reverse_jsframe_index]; + } + + // Helper function for heap object materialization. + Handle MaterializeNextHeapObject(); + Handle MaterializeNextValue(); + static void GenerateDeoptimizationEntries( MacroAssembler* masm, int count, BailoutType type); @@ -455,10 +488,22 @@ class Deoptimizer : public Malloced { // Array of output frame descriptions. FrameDescription** output_; + // Deferred values to be materialized. List deferred_objects_tagged_values_; List deferred_objects_double_values_; List deferred_objects_; List deferred_heap_numbers_; + + // Output frame information. Only used during heap object materialization. + List > jsframe_functions_; + List jsframe_has_adapted_arguments_; + + // Materialized objects. Only used during heap object materialization. + List >* materialized_values_; + List >* materialized_objects_; + int materialization_value_index_; + int materialization_object_index_; + #ifdef DEBUG DisallowHeapAllocation* disallow_heap_allocation_; #endif // DEBUG @@ -712,7 +757,9 @@ class Translation BASE_EMBEDDED { SETTER_STUB_FRAME, ARGUMENTS_ADAPTOR_FRAME, COMPILED_STUB_FRAME, + DUPLICATED_OBJECT, ARGUMENTS_OBJECT, + CAPTURED_OBJECT, REGISTER, INT32_REGISTER, UINT32_REGISTER, @@ -744,6 +791,8 @@ class Translation BASE_EMBEDDED { void BeginGetterStubFrame(int literal_id); void BeginSetterStubFrame(int literal_id); void BeginArgumentsObject(int args_length); + void BeginCapturedObject(int length); + void DuplicateObject(int object_index); void StoreRegister(Register reg); void StoreInt32Register(Register reg); void StoreUint32Register(Register reg); diff --git a/deps/v8/src/effects.h b/deps/v8/src/effects.h new file mode 100644 index 0000000..8e82363 --- /dev/null +++ b/deps/v8/src/effects.h @@ -0,0 +1,361 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_EFFECTS_H_ +#define V8_EFFECTS_H_ + +#include "v8.h" + +#include "types.h" + +namespace v8 { +namespace internal { + + +// A simple struct to represent (write) effects. A write is represented as a +// modification of type bounds (e.g. of a variable). +// +// An effect can either be definite, if the write is known to have taken place, +// or 'possible', if it was optional. The difference is relevant when composing +// effects. +// +// There are two ways to compose effects: sequentially (they happen one after +// the other) or alternatively (either one or the other happens). A definite +// effect cancels out any previous effect upon sequencing. A possible effect +// merges into a previous effect, i.e., type bounds are merged. Alternative +// composition always merges bounds. It yields a possible effect if at least +// one was only possible. +struct Effect { + enum Modality { POSSIBLE, DEFINITE }; + + Modality modality; + Bounds bounds; + + Effect() {} + Effect(Bounds b, Modality m = DEFINITE) : modality(m), bounds(b) {} + + // The unknown effect. + static Effect Unknown(Isolate* isolate) { + return Effect(Bounds::Unbounded(isolate), POSSIBLE); + } + + static Effect Forget(Isolate* isolate) { + return Effect(Bounds::Unbounded(isolate), DEFINITE); + } + + // Sequential composition, as in 'e1; e2'. + static Effect Seq(Effect e1, Effect e2, Isolate* isolate) { + if (e2.modality == DEFINITE) return e2; + return Effect(Bounds::Either(e1.bounds, e2.bounds, isolate), e1.modality); + } + + // Alternative composition, as in 'cond ? e1 : e2'. + static Effect Alt(Effect e1, Effect e2, Isolate* isolate) { + return Effect( + Bounds::Either(e1.bounds, e2.bounds, isolate), + e1.modality == POSSIBLE ? POSSIBLE : e2.modality); + } +}; + + +// Classes encapsulating sets of effects on variables. +// +// Effects maps variables to effects and supports sequential and alternative +// composition. +// +// NestedEffects is an incremental representation that supports persistence +// through functional extension. It represents the map as an adjoin of a list +// of maps, whose tail can be shared. +// +// Both classes provide similar interfaces, implemented in parts through the +// EffectsMixin below (using sandwich style, to work around the style guide's +// MI restriction). +// +// We also (ab)use Effects/NestedEffects as a representation for abstract +// store typings. In that case, only definite effects are of interest. + +template +class EffectsMixin: public Base { + public: + explicit EffectsMixin(Zone* zone) : Base(zone) {} + + Effect Lookup(Var var) { + Locator locator; + return this->Find(var, &locator) + ? locator.value() : Effect::Unknown(Base::isolate()); + } + + Bounds LookupBounds(Var var) { + Effect effect = Lookup(var); + return effect.modality == Effect::DEFINITE + ? effect.bounds : Bounds::Unbounded(Base::isolate()); + } + + // Sequential composition. + void Seq(Var var, Effect effect) { + Locator locator; + if (!this->Insert(var, &locator)) { + effect = Effect::Seq(locator.value(), effect, Base::isolate()); + } + locator.set_value(effect); + } + + void Seq(Effects that) { + SeqMerger merge = { *this }; + that.ForEach(&merge); + } + + // Alternative composition. + void Alt(Var var, Effect effect) { + Locator locator; + if (!this->Insert(var, &locator)) { + effect = Effect::Alt(locator.value(), effect, Base::isolate()); + } + locator.set_value(effect); + } + + void Alt(Effects that) { + AltWeakener weaken = { *this, that }; + this->ForEach(&weaken); + AltMerger merge = { *this }; + that.ForEach(&merge); + } + + // Invalidation. + void Forget() { + Overrider override = { + Effect::Forget(Base::isolate()), Effects(Base::zone()) }; + this->ForEach(&override); + Seq(override.effects); + } + + protected: + typedef typename Base::Locator Locator; + + template + struct SeqMerger { + void Call(Var var, Effect effect) { self.Seq(var, effect); } + Self self; + }; + + template + struct AltMerger { + void Call(Var var, Effect effect) { self.Alt(var, effect); } + Self self; + }; + + template + struct AltWeakener { + void Call(Var var, Effect effect) { + if (effect.modality == Effect::DEFINITE && !other.Contains(var)) { + effect.modality = Effect::POSSIBLE; + Locator locator; + self.Insert(var, &locator); + locator.set_value(effect); + } + } + Self self; + Effects other; + }; + + struct Overrider { + void Call(Var var, Effect effect) { effects.Seq(var, new_effect); } + Effect new_effect; + Effects effects; + }; +}; + + +template class Effects; +template class NestedEffectsBase; + +template +class EffectsBase { + public: + explicit EffectsBase(Zone* zone) : map_(new(zone) Mapping(zone)) {} + + bool IsEmpty() { return map_->is_empty(); } + + protected: + friend class NestedEffectsBase; + friend class + EffectsMixin, Effects >; + + Zone* zone() { return map_->allocator().zone(); } + Isolate* isolate() { return zone()->isolate(); } + + struct SplayTreeConfig { + typedef Var Key; + typedef Effect Value; + static const Var kNoKey = kNoVar; + static Effect NoValue() { return Effect(); } + static int Compare(int x, int y) { return y - x; } + }; + typedef ZoneSplayTree Mapping; + typedef typename Mapping::Locator Locator; + + bool Contains(Var var) { + ASSERT(var != kNoVar); + return map_->Contains(var); + } + bool Find(Var var, Locator* locator) { + ASSERT(var != kNoVar); + return map_->Find(var, locator); + } + bool Insert(Var var, Locator* locator) { + ASSERT(var != kNoVar); + return map_->Insert(var, locator); + } + + template + void ForEach(Callback* callback) { + return map_->ForEach(callback); + } + + private: + Mapping* map_; +}; + +template +const Var EffectsBase::SplayTreeConfig::kNoKey; + +template +class Effects: public + EffectsMixin, Effects > { + public: + explicit Effects(Zone* zone) + : EffectsMixin, Effects >(zone) + {} +}; + + +template +class NestedEffectsBase { + public: + explicit NestedEffectsBase(Zone* zone) : node_(new(zone) Node(zone)) {} + + template + void ForEach(Callback* callback) { + if (node_->previous) NestedEffectsBase(node_->previous).ForEach(callback); + node_->effects.ForEach(callback); + } + + Effects Top() { return node_->effects; } + + bool IsEmpty() { + for (Node* node = node_; node != NULL; node = node->previous) { + if (!node->effects.IsEmpty()) return false; + } + return true; + } + + protected: + typedef typename EffectsBase::Locator Locator; + + Zone* zone() { return node_->zone; } + Isolate* isolate() { return zone()->isolate(); } + + void push() { node_ = new(node_->zone) Node(node_->zone, node_); } + void pop() { node_ = node_->previous; } + bool is_empty() { return node_ == NULL; } + + bool Contains(Var var) { + ASSERT(var != kNoVar); + for (Node* node = node_; node != NULL; node = node->previous) { + if (node->effects.Contains(var)) return true; + } + return false; + } + + bool Find(Var var, Locator* locator) { + ASSERT(var != kNoVar); + for (Node* node = node_; node != NULL; node = node->previous) { + if (node->effects.Find(var, locator)) return true; + } + return false; + } + + bool Insert(Var var, Locator* locator); + + private: + struct Node: ZoneObject { + Zone* zone; + Effects effects; + Node* previous; + explicit Node(Zone* zone, Node* previous = NULL) + : zone(zone), effects(zone), previous(previous) {} + }; + + explicit NestedEffectsBase(Node* node) : node_(node) {} + + Node* node_; +}; + + +template +bool NestedEffectsBase::Insert(Var var, Locator* locator) { + ASSERT(var != kNoVar); + if (!node_->effects.Insert(var, locator)) return false; + Locator shadowed; + for (Node* node = node_->previous; node != NULL; node = node->previous) { + if (node->effects.Find(var, &shadowed)) { + // Initialize with shadowed entry. + locator->set_value(shadowed.value()); + return false; + } + } + return true; +} + + +template +class NestedEffects: public + EffectsMixin, Effects > { + public: + explicit NestedEffects(Zone* zone) : + EffectsMixin, Effects >( + zone) {} + + // Create an extension of the current effect set. The current set should not + // be modified while the extension is in use. + NestedEffects Push() { + NestedEffects result = *this; + result.push(); + return result; + } + + NestedEffects Pop() { + NestedEffects result = *this; + result.pop(); + ASSERT(!this->is_empty()); + return result; + } +}; + +} } // namespace v8::internal + +#endif // V8_EFFECTS_H_ diff --git a/deps/v8/src/execution.cc b/deps/v8/src/execution.cc index d7b9cf5..ecfa1db 100644 --- a/deps/v8/src/execution.cc +++ b/deps/v8/src/execution.cc @@ -206,10 +206,12 @@ Handle Execution::TryCall(Handle func, catcher.SetCaptureMessage(false); *caught_exception = false; + // Get isolate now, because handle might be persistent + // and get destroyed in the next call. + Isolate* isolate = func->GetIsolate(); Handle result = Invoke(false, func, receiver, argc, args, caught_exception); - Isolate* isolate = func->GetIsolate(); if (*caught_exception) { ASSERT(catcher.HasCaught()); ASSERT(isolate->has_pending_exception()); diff --git a/deps/v8/src/extensions/i18n/collator.cc b/deps/v8/src/extensions/i18n/collator.cc deleted file mode 100644 index 61b1d63..0000000 --- a/deps/v8/src/extensions/i18n/collator.cc +++ /dev/null @@ -1,366 +0,0 @@ -// Copyright 2013 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// limitations under the License. - -#include "collator.h" - -#include "i18n-utils.h" -#include "unicode/coll.h" -#include "unicode/locid.h" -#include "unicode/ucol.h" - -namespace v8_i18n { - -static icu::Collator* InitializeCollator( - v8::Handle, v8::Handle, v8::Handle); - -static icu::Collator* CreateICUCollator( - const icu::Locale&, v8::Handle); - -static bool SetBooleanAttribute( - UColAttribute, const char*, v8::Handle, icu::Collator*); - -static void SetResolvedSettings( - const icu::Locale&, icu::Collator*, v8::Handle); - -static void SetBooleanSetting( - UColAttribute, icu::Collator*, const char*, v8::Handle); - -icu::Collator* Collator::UnpackCollator(v8::Handle obj) { - v8::HandleScope handle_scope; - - if (obj->HasOwnProperty(v8::String::New("collator"))) { - return static_cast( - obj->GetAlignedPointerFromInternalField(0)); - } - - return NULL; -} - -void Collator::DeleteCollator(v8::Isolate* isolate, - v8::Persistent* object, - void* param) { - // First delete the hidden C++ object. - // Unpacking should never return NULL here. That would only happen if - // this method is used as the weak callback for persistent handles not - // pointing to a collator. - v8::HandleScope handle_scope(isolate); - v8::Local handle = v8::Local::New(isolate, *object); - delete UnpackCollator(handle); - - // Then dispose of the persistent handle to JS object. - object->Dispose(isolate); -} - - -// Throws a JavaScript exception. -static v8::Handle ThrowUnexpectedObjectError() { - // Returns undefined, and schedules an exception to be thrown. - return v8::ThrowException(v8::Exception::Error( - v8::String::New("Collator method called on an object " - "that is not a Collator."))); -} - - -// When there's an ICU error, throw a JavaScript error with |message|. -static v8::Handle ThrowExceptionForICUError(const char* message) { - return v8::ThrowException(v8::Exception::Error(v8::String::New(message))); -} - - -// static -void Collator::JSInternalCompare( - const v8::FunctionCallbackInfo& args) { - if (args.Length() != 3 || !args[0]->IsObject() || - !args[1]->IsString() || !args[2]->IsString()) { - v8::ThrowException(v8::Exception::SyntaxError( - v8::String::New("Collator and two string arguments are required."))); - return; - } - - icu::Collator* collator = UnpackCollator(args[0]->ToObject()); - if (!collator) { - ThrowUnexpectedObjectError(); - return; - } - - v8::String::Value string_value1(args[1]); - v8::String::Value string_value2(args[2]); - const UChar* string1 = reinterpret_cast(*string_value1); - const UChar* string2 = reinterpret_cast(*string_value2); - UErrorCode status = U_ZERO_ERROR; - UCollationResult result = collator->compare( - string1, string_value1.length(), string2, string_value2.length(), status); - - if (U_FAILURE(status)) { - ThrowExceptionForICUError( - "Internal error. Unexpected failure in Collator.compare."); - return; - } - - args.GetReturnValue().Set(result); -} - -void Collator::JSCreateCollator( - const v8::FunctionCallbackInfo& args) { - if (args.Length() != 3 || !args[0]->IsString() || !args[1]->IsObject() || - !args[2]->IsObject()) { - v8::ThrowException(v8::Exception::SyntaxError( - v8::String::New("Internal error, wrong parameters."))); - return; - } - - v8::Isolate* isolate = args.GetIsolate(); - v8::Local intl_collator_template = - Utils::GetTemplate(isolate); - - // Create an empty object wrapper. - v8::Local local_object = intl_collator_template->NewInstance(); - // But the handle shouldn't be empty. - // That can happen if there was a stack overflow when creating the object. - if (local_object.IsEmpty()) { - args.GetReturnValue().Set(local_object); - return; - } - - // Set collator as internal field of the resulting JS object. - icu::Collator* collator = InitializeCollator( - args[0]->ToString(), args[1]->ToObject(), args[2]->ToObject()); - - if (!collator) { - v8::ThrowException(v8::Exception::Error(v8::String::New( - "Internal error. Couldn't create ICU collator."))); - return; - } else { - local_object->SetAlignedPointerInInternalField(0, collator); - - // Make it safer to unpack later on. - v8::TryCatch try_catch; - local_object->Set(v8::String::New("collator"), v8::String::New("valid")); - if (try_catch.HasCaught()) { - v8::ThrowException(v8::Exception::Error( - v8::String::New("Internal error, couldn't set property."))); - return; - } - } - - v8::Persistent wrapper(isolate, local_object); - // Make object handle weak so we can delete iterator once GC kicks in. - wrapper.MakeWeak(NULL, &DeleteCollator); - args.GetReturnValue().Set(wrapper); - wrapper.ClearAndLeak(); -} - -static icu::Collator* InitializeCollator(v8::Handle locale, - v8::Handle options, - v8::Handle resolved) { - // Convert BCP47 into ICU locale format. - UErrorCode status = U_ZERO_ERROR; - icu::Locale icu_locale; - char icu_result[ULOC_FULLNAME_CAPACITY]; - int icu_length = 0; - v8::String::AsciiValue bcp47_locale(locale); - if (bcp47_locale.length() != 0) { - uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, - &icu_length, &status); - if (U_FAILURE(status) || icu_length == 0) { - return NULL; - } - icu_locale = icu::Locale(icu_result); - } - - icu::Collator* collator = CreateICUCollator(icu_locale, options); - if (!collator) { - // Remove extensions and try again. - icu::Locale no_extension_locale(icu_locale.getBaseName()); - collator = CreateICUCollator(no_extension_locale, options); - - // Set resolved settings (pattern, numbering system). - SetResolvedSettings(no_extension_locale, collator, resolved); - } else { - SetResolvedSettings(icu_locale, collator, resolved); - } - - return collator; -} - -static icu::Collator* CreateICUCollator( - const icu::Locale& icu_locale, v8::Handle options) { - // Make collator from options. - icu::Collator* collator = NULL; - UErrorCode status = U_ZERO_ERROR; - collator = icu::Collator::createInstance(icu_locale, status); - - if (U_FAILURE(status)) { - delete collator; - return NULL; - } - - // Set flags first, and then override them with sensitivity if necessary. - SetBooleanAttribute(UCOL_NUMERIC_COLLATION, "numeric", options, collator); - - // Normalization is always on, by the spec. We are free to optimize - // if the strings are already normalized (but we don't have a way to tell - // that right now). - collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status); - - icu::UnicodeString case_first; - if (Utils::ExtractStringSetting(options, "caseFirst", &case_first)) { - if (case_first == UNICODE_STRING_SIMPLE("upper")) { - collator->setAttribute(UCOL_CASE_FIRST, UCOL_UPPER_FIRST, status); - } else if (case_first == UNICODE_STRING_SIMPLE("lower")) { - collator->setAttribute(UCOL_CASE_FIRST, UCOL_LOWER_FIRST, status); - } else { - // Default (false/off). - collator->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status); - } - } - - icu::UnicodeString sensitivity; - if (Utils::ExtractStringSetting(options, "sensitivity", &sensitivity)) { - if (sensitivity == UNICODE_STRING_SIMPLE("base")) { - collator->setStrength(icu::Collator::PRIMARY); - } else if (sensitivity == UNICODE_STRING_SIMPLE("accent")) { - collator->setStrength(icu::Collator::SECONDARY); - } else if (sensitivity == UNICODE_STRING_SIMPLE("case")) { - collator->setStrength(icu::Collator::PRIMARY); - collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status); - } else { - // variant (default) - collator->setStrength(icu::Collator::TERTIARY); - } - } - - bool ignore; - if (Utils::ExtractBooleanSetting(options, "ignorePunctuation", &ignore)) { - if (ignore) { - collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status); - } - } - - return collator; -} - -static bool SetBooleanAttribute(UColAttribute attribute, - const char* name, - v8::Handle options, - icu::Collator* collator) { - UErrorCode status = U_ZERO_ERROR; - bool result; - if (Utils::ExtractBooleanSetting(options, name, &result)) { - collator->setAttribute(attribute, result ? UCOL_ON : UCOL_OFF, status); - if (U_FAILURE(status)) { - return false; - } - } - - return true; -} - -static void SetResolvedSettings(const icu::Locale& icu_locale, - icu::Collator* collator, - v8::Handle resolved) { - SetBooleanSetting(UCOL_NUMERIC_COLLATION, collator, "numeric", resolved); - - UErrorCode status = U_ZERO_ERROR; - - switch (collator->getAttribute(UCOL_CASE_FIRST, status)) { - case UCOL_LOWER_FIRST: - resolved->Set(v8::String::New("caseFirst"), v8::String::New("lower")); - break; - case UCOL_UPPER_FIRST: - resolved->Set(v8::String::New("caseFirst"), v8::String::New("upper")); - break; - default: - resolved->Set(v8::String::New("caseFirst"), v8::String::New("false")); - } - - switch (collator->getAttribute(UCOL_STRENGTH, status)) { - case UCOL_PRIMARY: { - resolved->Set(v8::String::New("strength"), v8::String::New("primary")); - - // case level: true + s1 -> case, s1 -> base. - if (UCOL_ON == collator->getAttribute(UCOL_CASE_LEVEL, status)) { - resolved->Set(v8::String::New("sensitivity"), v8::String::New("case")); - } else { - resolved->Set(v8::String::New("sensitivity"), v8::String::New("base")); - } - break; - } - case UCOL_SECONDARY: - resolved->Set(v8::String::New("strength"), v8::String::New("secondary")); - resolved->Set(v8::String::New("sensitivity"), v8::String::New("accent")); - break; - case UCOL_TERTIARY: - resolved->Set(v8::String::New("strength"), v8::String::New("tertiary")); - resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant")); - break; - case UCOL_QUATERNARY: - // We shouldn't get quaternary and identical from ICU, but if we do - // put them into variant. - resolved->Set(v8::String::New("strength"), v8::String::New("quaternary")); - resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant")); - break; - default: - resolved->Set(v8::String::New("strength"), v8::String::New("identical")); - resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant")); - } - - if (UCOL_SHIFTED == collator->getAttribute(UCOL_ALTERNATE_HANDLING, status)) { - resolved->Set(v8::String::New("ignorePunctuation"), - v8::Boolean::New(true)); - } else { - resolved->Set(v8::String::New("ignorePunctuation"), - v8::Boolean::New(false)); - } - - // Set the locale - char result[ULOC_FULLNAME_CAPACITY]; - status = U_ZERO_ERROR; - uloc_toLanguageTag( - icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status); - if (U_SUCCESS(status)) { - resolved->Set(v8::String::New("locale"), v8::String::New(result)); - } else { - // This would never happen, since we got the locale from ICU. - resolved->Set(v8::String::New("locale"), v8::String::New("und")); - } -} - -static void SetBooleanSetting(UColAttribute attribute, - icu::Collator* collator, - const char* property, - v8::Handle resolved) { - UErrorCode status = U_ZERO_ERROR; - if (UCOL_ON == collator->getAttribute(attribute, status)) { - resolved->Set(v8::String::New(property), v8::Boolean::New(true)); - } else { - resolved->Set(v8::String::New(property), v8::Boolean::New(false)); - } -} - -} // namespace v8_i18n diff --git a/deps/v8/src/extensions/i18n/collator.js b/deps/v8/src/extensions/i18n/collator.js index 3483515..d8d247b 100644 --- a/deps/v8/src/extensions/i18n/collator.js +++ b/deps/v8/src/extensions/i18n/collator.js @@ -35,8 +35,6 @@ * Useful for subclassing. */ function initializeCollator(collator, locales, options) { - native function NativeJSCreateCollator(); - if (collator.hasOwnProperty('__initializedIntlObject')) { throw new TypeError('Trying to re-initialize Collator object.'); } @@ -103,9 +101,9 @@ function initializeCollator(collator, locales, options) { usage: {value: internalOptions.usage, writable: true} }); - var internalCollator = NativeJSCreateCollator(requestedLocale, - internalOptions, - resolved); + var internalCollator = %CreateCollator(requestedLocale, + internalOptions, + resolved); // Writable, configurable and enumerable are set to false by default. Object.defineProperty(collator, 'collator', {value: internalCollator}); @@ -204,8 +202,7 @@ function initializeCollator(collator, locales, options) { * the sort order, or x comes after y in the sort order, respectively. */ function compare(collator, x, y) { - native function NativeJSInternalCompare(); - return NativeJSInternalCompare(collator.collator, String(x), String(y)); + return %InternalCompare(collator.collator, String(x), String(y)); }; diff --git a/deps/v8/src/extensions/i18n/i18n-extension.cc b/deps/v8/src/extensions/i18n/i18n-extension.cc index b110b7d..e2cba8e 100644 --- a/deps/v8/src/extensions/i18n/i18n-extension.cc +++ b/deps/v8/src/extensions/i18n/i18n-extension.cc @@ -29,9 +29,7 @@ #include "i18n-extension.h" #include "break-iterator.h" -#include "collator.h" #include "natives.h" -#include "number-format.h" using v8::internal::I18NNatives; @@ -47,22 +45,6 @@ Extension::Extension() v8::Handle Extension::GetNativeFunction( v8::Handle name) { - // Number format and parse. - if (name->Equals(v8::String::New("NativeJSCreateNumberFormat"))) { - return v8::FunctionTemplate::New(NumberFormat::JSCreateNumberFormat); - } else if (name->Equals(v8::String::New("NativeJSInternalNumberFormat"))) { - return v8::FunctionTemplate::New(NumberFormat::JSInternalFormat); - } else if (name->Equals(v8::String::New("NativeJSInternalNumberParse"))) { - return v8::FunctionTemplate::New(NumberFormat::JSInternalParse); - } - - // Collator. - if (name->Equals(v8::String::New("NativeJSCreateCollator"))) { - return v8::FunctionTemplate::New(Collator::JSCreateCollator); - } else if (name->Equals(v8::String::New("NativeJSInternalCompare"))) { - return v8::FunctionTemplate::New(Collator::JSInternalCompare); - } - // Break iterator. if (name->Equals(v8::String::New("NativeJSCreateBreakIterator"))) { return v8::FunctionTemplate::New(BreakIterator::JSCreateBreakIterator); diff --git a/deps/v8/src/extensions/i18n/number-format.cc b/deps/v8/src/extensions/i18n/number-format.cc deleted file mode 100644 index 1364715..0000000 --- a/deps/v8/src/extensions/i18n/number-format.cc +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright 2013 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// limitations under the License. - -#include "number-format.h" - -#include - -#include "i18n-utils.h" -#include "unicode/curramt.h" -#include "unicode/dcfmtsym.h" -#include "unicode/decimfmt.h" -#include "unicode/locid.h" -#include "unicode/numfmt.h" -#include "unicode/numsys.h" -#include "unicode/uchar.h" -#include "unicode/ucurr.h" -#include "unicode/unum.h" -#include "unicode/uversion.h" - -namespace v8_i18n { - -static icu::DecimalFormat* InitializeNumberFormat(v8::Handle, - v8::Handle, - v8::Handle); -static icu::DecimalFormat* CreateICUNumberFormat(const icu::Locale&, - v8::Handle); -static void SetResolvedSettings(const icu::Locale&, - icu::DecimalFormat*, - v8::Handle); - -icu::DecimalFormat* NumberFormat::UnpackNumberFormat( - v8::Handle obj) { - v8::HandleScope handle_scope; - - // v8::ObjectTemplate doesn't have HasInstance method so we can't check - // if obj is an instance of NumberFormat class. We'll check for a property - // that has to be in the object. The same applies to other services, like - // Collator and DateTimeFormat. - if (obj->HasOwnProperty(v8::String::New("numberFormat"))) { - return static_cast( - obj->GetAlignedPointerFromInternalField(0)); - } - - return NULL; -} - -void NumberFormat::DeleteNumberFormat(v8::Isolate* isolate, - v8::Persistent* object, - void* param) { - // First delete the hidden C++ object. - // Unpacking should never return NULL here. That would only happen if - // this method is used as the weak callback for persistent handles not - // pointing to a date time formatter. - v8::HandleScope handle_scope(isolate); - v8::Local handle = v8::Local::New(isolate, *object); - delete UnpackNumberFormat(handle); - - // Then dispose of the persistent handle to JS object. - object->Dispose(isolate); -} - -void NumberFormat::JSInternalFormat( - const v8::FunctionCallbackInfo& args) { - if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsNumber()) { - v8::ThrowException(v8::Exception::Error( - v8::String::New("Formatter and numeric value have to be specified."))); - return; - } - - icu::DecimalFormat* number_format = UnpackNumberFormat(args[0]->ToObject()); - if (!number_format) { - v8::ThrowException(v8::Exception::Error( - v8::String::New("NumberFormat method called on an object " - "that is not a NumberFormat."))); - return; - } - - // ICU will handle actual NaN value properly and return NaN string. - icu::UnicodeString result; - number_format->format(args[1]->NumberValue(), result); - - args.GetReturnValue().Set(v8::String::New( - reinterpret_cast(result.getBuffer()), result.length())); -} - -void NumberFormat::JSInternalParse( - const v8::FunctionCallbackInfo& args) { - if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsString()) { - v8::ThrowException(v8::Exception::Error( - v8::String::New("Formatter and string have to be specified."))); - return; - } - - icu::DecimalFormat* number_format = UnpackNumberFormat(args[0]->ToObject()); - if (!number_format) { - v8::ThrowException(v8::Exception::Error( - v8::String::New("NumberFormat method called on an object " - "that is not a NumberFormat."))); - return; - } - - // ICU will handle actual NaN value properly and return NaN string. - icu::UnicodeString string_number; - if (!Utils::V8StringToUnicodeString(args[1]->ToString(), &string_number)) { - string_number = ""; - } - - UErrorCode status = U_ZERO_ERROR; - icu::Formattable result; - // ICU 4.6 doesn't support parseCurrency call. We need to wait for ICU49 - // to be part of Chrome. - // TODO(cira): Include currency parsing code using parseCurrency call. - // We need to check if the formatter parses all currencies or only the - // one it was constructed with (it will impact the API - how to return ISO - // code and the value). - number_format->parse(string_number, result, status); - if (U_FAILURE(status)) { - return; - } - - switch (result.getType()) { - case icu::Formattable::kDouble: - args.GetReturnValue().Set(result.getDouble()); - return; - case icu::Formattable::kLong: - args.GetReturnValue().Set(result.getLong()); - return; - case icu::Formattable::kInt64: - args.GetReturnValue().Set(static_cast(result.getInt64())); - return; - default: - return; - } -} - -void NumberFormat::JSCreateNumberFormat( - const v8::FunctionCallbackInfo& args) { - if (args.Length() != 3 || - !args[0]->IsString() || - !args[1]->IsObject() || - !args[2]->IsObject()) { - v8::ThrowException(v8::Exception::Error( - v8::String::New("Internal error, wrong parameters."))); - return; - } - - v8::Isolate* isolate = args.GetIsolate(); - v8::Local number_format_template = - Utils::GetTemplate(isolate); - - // Create an empty object wrapper. - v8::Local local_object = number_format_template->NewInstance(); - // But the handle shouldn't be empty. - // That can happen if there was a stack overflow when creating the object. - if (local_object.IsEmpty()) { - args.GetReturnValue().Set(local_object); - return; - } - - // Set number formatter as internal field of the resulting JS object. - icu::DecimalFormat* number_format = InitializeNumberFormat( - args[0]->ToString(), args[1]->ToObject(), args[2]->ToObject()); - - if (!number_format) { - v8::ThrowException(v8::Exception::Error(v8::String::New( - "Internal error. Couldn't create ICU number formatter."))); - return; - } else { - local_object->SetAlignedPointerInInternalField(0, number_format); - - v8::TryCatch try_catch; - local_object->Set(v8::String::New("numberFormat"), - v8::String::New("valid")); - if (try_catch.HasCaught()) { - v8::ThrowException(v8::Exception::Error( - v8::String::New("Internal error, couldn't set property."))); - return; - } - } - - v8::Persistent wrapper(isolate, local_object); - // Make object handle weak so we can delete iterator once GC kicks in. - wrapper.MakeWeak(NULL, &DeleteNumberFormat); - args.GetReturnValue().Set(wrapper); - wrapper.ClearAndLeak(); -} - -static icu::DecimalFormat* InitializeNumberFormat( - v8::Handle locale, - v8::Handle options, - v8::Handle resolved) { - // Convert BCP47 into ICU locale format. - UErrorCode status = U_ZERO_ERROR; - icu::Locale icu_locale; - char icu_result[ULOC_FULLNAME_CAPACITY]; - int icu_length = 0; - v8::String::AsciiValue bcp47_locale(locale); - if (bcp47_locale.length() != 0) { - uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, - &icu_length, &status); - if (U_FAILURE(status) || icu_length == 0) { - return NULL; - } - icu_locale = icu::Locale(icu_result); - } - - icu::DecimalFormat* number_format = - CreateICUNumberFormat(icu_locale, options); - if (!number_format) { - // Remove extensions and try again. - icu::Locale no_extension_locale(icu_locale.getBaseName()); - number_format = CreateICUNumberFormat(no_extension_locale, options); - - // Set resolved settings (pattern, numbering system). - SetResolvedSettings(no_extension_locale, number_format, resolved); - } else { - SetResolvedSettings(icu_locale, number_format, resolved); - } - - return number_format; -} - -static icu::DecimalFormat* CreateICUNumberFormat( - const icu::Locale& icu_locale, v8::Handle options) { - // Make formatter from options. Numbering system is added - // to the locale as Unicode extension (if it was specified at all). - UErrorCode status = U_ZERO_ERROR; - icu::DecimalFormat* number_format = NULL; - icu::UnicodeString style; - icu::UnicodeString currency; - if (Utils::ExtractStringSetting(options, "style", &style)) { - if (style == UNICODE_STRING_SIMPLE("currency")) { - Utils::ExtractStringSetting(options, "currency", ¤cy); - - icu::UnicodeString display; - Utils::ExtractStringSetting(options, "currencyDisplay", &display); -#if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6) - icu::NumberFormat::EStyles style; - if (display == UNICODE_STRING_SIMPLE("code")) { - style = icu::NumberFormat::kIsoCurrencyStyle; - } else if (display == UNICODE_STRING_SIMPLE("name")) { - style = icu::NumberFormat::kPluralCurrencyStyle; - } else { - style = icu::NumberFormat::kCurrencyStyle; - } -#else // ICU version is 4.8 or above (we ignore versions below 4.0). - UNumberFormatStyle style; - if (display == UNICODE_STRING_SIMPLE("code")) { - style = UNUM_CURRENCY_ISO; - } else if (display == UNICODE_STRING_SIMPLE("name")) { - style = UNUM_CURRENCY_PLURAL; - } else { - style = UNUM_CURRENCY; - } -#endif - - number_format = static_cast( - icu::NumberFormat::createInstance(icu_locale, style, status)); - } else if (style == UNICODE_STRING_SIMPLE("percent")) { - number_format = static_cast( - icu::NumberFormat::createPercentInstance(icu_locale, status)); - if (U_FAILURE(status)) { - delete number_format; - return NULL; - } - // Make sure 1.1% doesn't go into 2%. - number_format->setMinimumFractionDigits(1); - } else { - // Make a decimal instance by default. - number_format = static_cast( - icu::NumberFormat::createInstance(icu_locale, status)); - } - } - - if (U_FAILURE(status)) { - delete number_format; - return NULL; - } - - // Set all options. - if (!currency.isEmpty()) { - number_format->setCurrency(currency.getBuffer(), status); - } - - int32_t digits; - if (Utils::ExtractIntegerSetting( - options, "minimumIntegerDigits", &digits)) { - number_format->setMinimumIntegerDigits(digits); - } - - if (Utils::ExtractIntegerSetting( - options, "minimumFractionDigits", &digits)) { - number_format->setMinimumFractionDigits(digits); - } - - if (Utils::ExtractIntegerSetting( - options, "maximumFractionDigits", &digits)) { - number_format->setMaximumFractionDigits(digits); - } - - bool significant_digits_used = false; - if (Utils::ExtractIntegerSetting( - options, "minimumSignificantDigits", &digits)) { - number_format->setMinimumSignificantDigits(digits); - significant_digits_used = true; - } - - if (Utils::ExtractIntegerSetting( - options, "maximumSignificantDigits", &digits)) { - number_format->setMaximumSignificantDigits(digits); - significant_digits_used = true; - } - - number_format->setSignificantDigitsUsed(significant_digits_used); - - bool grouping; - if (Utils::ExtractBooleanSetting(options, "useGrouping", &grouping)) { - number_format->setGroupingUsed(grouping); - } - - // Set rounding mode. - number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp); - - return number_format; -} - -static void SetResolvedSettings(const icu::Locale& icu_locale, - icu::DecimalFormat* number_format, - v8::Handle resolved) { - icu::UnicodeString pattern; - number_format->toPattern(pattern); - resolved->Set(v8::String::New("pattern"), - v8::String::New(reinterpret_cast( - pattern.getBuffer()), pattern.length())); - - // Set resolved currency code in options.currency if not empty. - icu::UnicodeString currency(number_format->getCurrency()); - if (!currency.isEmpty()) { - resolved->Set(v8::String::New("currency"), - v8::String::New(reinterpret_cast( - currency.getBuffer()), currency.length())); - } - - // Ugly hack. ICU doesn't expose numbering system in any way, so we have - // to assume that for given locale NumberingSystem constructor produces the - // same digits as NumberFormat would. - UErrorCode status = U_ZERO_ERROR; - icu::NumberingSystem* numbering_system = - icu::NumberingSystem::createInstance(icu_locale, status); - if (U_SUCCESS(status)) { - const char* ns = numbering_system->getName(); - resolved->Set(v8::String::New("numberingSystem"), v8::String::New(ns)); - } else { - resolved->Set(v8::String::New("numberingSystem"), v8::Undefined()); - } - delete numbering_system; - - resolved->Set(v8::String::New("useGrouping"), - v8::Boolean::New(number_format->isGroupingUsed())); - - resolved->Set(v8::String::New("minimumIntegerDigits"), - v8::Integer::New(number_format->getMinimumIntegerDigits())); - - resolved->Set(v8::String::New("minimumFractionDigits"), - v8::Integer::New(number_format->getMinimumFractionDigits())); - - resolved->Set(v8::String::New("maximumFractionDigits"), - v8::Integer::New(number_format->getMaximumFractionDigits())); - - if (resolved->HasOwnProperty(v8::String::New("minimumSignificantDigits"))) { - resolved->Set(v8::String::New("minimumSignificantDigits"), v8::Integer::New( - number_format->getMinimumSignificantDigits())); - } - - if (resolved->HasOwnProperty(v8::String::New("maximumSignificantDigits"))) { - resolved->Set(v8::String::New("maximumSignificantDigits"), v8::Integer::New( - number_format->getMaximumSignificantDigits())); - } - - // Set the locale - char result[ULOC_FULLNAME_CAPACITY]; - status = U_ZERO_ERROR; - uloc_toLanguageTag( - icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status); - if (U_SUCCESS(status)) { - resolved->Set(v8::String::New("locale"), v8::String::New(result)); - } else { - // This would never happen, since we got the locale from ICU. - resolved->Set(v8::String::New("locale"), v8::String::New("und")); - } -} - -} // namespace v8_i18n diff --git a/deps/v8/src/extensions/i18n/number-format.js b/deps/v8/src/extensions/i18n/number-format.js index 1cd3db1..5722a5d 100644 --- a/deps/v8/src/extensions/i18n/number-format.js +++ b/deps/v8/src/extensions/i18n/number-format.js @@ -65,8 +65,6 @@ function getNumberOption(options, property, min, max, fallback) { * Useful for subclassing. */ function initializeNumberFormat(numberFormat, locales, options) { - native function NativeJSCreateNumberFormat(); - if (numberFormat.hasOwnProperty('__initializedIntlObject')) { throw new TypeError('Trying to re-initialize NumberFormat object.'); } @@ -148,9 +146,9 @@ function initializeNumberFormat(numberFormat, locales, options) { if (internalOptions.hasOwnProperty('maximumSignificantDigits')) { defineWEProperty(resolved, 'maximumSignificantDigits', undefined); } - var formatter = NativeJSCreateNumberFormat(requestedLocale, - internalOptions, - resolved); + var formatter = %CreateNumberFormat(requestedLocale, + internalOptions, + resolved); // We can't get information about number or currency style from ICU, so we // assume user request was fulfilled. @@ -269,15 +267,13 @@ function initializeNumberFormat(numberFormat, locales, options) { * NumberFormat. */ function formatNumber(formatter, value) { - native function NativeJSInternalNumberFormat(); - // Spec treats -0 and +0 as 0. var number = Number(value); if (number === -0) { number = 0; } - return NativeJSInternalNumberFormat(formatter.formatter, number); + return %InternalNumberFormat(formatter.formatter, number); } @@ -285,9 +281,7 @@ function formatNumber(formatter, value) { * Returns a Number that represents string value that was passed in. */ function parseNumber(formatter, value) { - native function NativeJSInternalNumberParse(); - - return NativeJSInternalNumberParse(formatter.formatter, String(value)); + return %InternalNumberParse(formatter.formatter, String(value)); } diff --git a/deps/v8/src/factory.cc b/deps/v8/src/factory.cc index 3ca0efa..9323c2f 100644 --- a/deps/v8/src/factory.cc +++ b/deps/v8/src/factory.cc @@ -1023,10 +1023,11 @@ Handle Factory::NewGlobalObject( Handle Factory::NewJSObjectFromMap(Handle map, - PretenureFlag pretenure) { + PretenureFlag pretenure, + bool alloc_props) { CALL_HEAP_FUNCTION( isolate(), - isolate()->heap()->AllocateJSObjectFromMap(*map, pretenure), + isolate()->heap()->AllocateJSObjectFromMap(*map, pretenure, alloc_props), JSObject); } @@ -1215,6 +1216,7 @@ Handle Factory::NewSharedFunctionInfo( shared->set_num_literals(literals_array_size); if (is_generator) { shared->set_instance_class_name(isolate()->heap()->Generator_string()); + shared->DisableOptimization(kGenerator); } return shared; } @@ -1391,8 +1393,10 @@ Handle Factory::CreateApiFunction( Smi::cast(instance_template->internal_field_count())->value(); } + // TODO(svenpanne) Kill ApiInstanceType and refactor things by generalizing + // JSObject::GetHeaderSize. int instance_size = kPointerSize * internal_field_count; - InstanceType type = INVALID_TYPE; + InstanceType type; switch (instance_type) { case JavaScriptObject: type = JS_OBJECT_TYPE; @@ -1407,9 +1411,10 @@ Handle Factory::CreateApiFunction( instance_size += JSGlobalProxy::kSize; break; default: + UNREACHABLE(); + type = JS_OBJECT_TYPE; // Keep the compiler happy. break; } - ASSERT(type != INVALID_TYPE); Handle result = NewFunction(Factory::empty_string(), diff --git a/deps/v8/src/factory.h b/deps/v8/src/factory.h index dc7933a..02c9a4d 100644 --- a/deps/v8/src/factory.h +++ b/deps/v8/src/factory.h @@ -301,7 +301,11 @@ class Factory { // JS objects are pretenured when allocated by the bootstrapper and // runtime. Handle NewJSObjectFromMap(Handle map, - PretenureFlag pretenure = NOT_TENURED); + PretenureFlag pretenure = NOT_TENURED, + bool allocate_properties = true); + + Handle NewJSObjectFromMapForDeoptimizer( + Handle map, PretenureFlag pretenure = NOT_TENURED); // JS modules are pretenured. Handle NewJSModule(Handle context, diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index c68beb5..c0ad4a8 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -264,6 +264,8 @@ DEFINE_int(deopt_every_n_garbage_collections, "deoptimize every n garbage collections") DEFINE_bool(print_deopt_stress, false, "print number of possible deopt points") DEFINE_bool(trap_on_deopt, false, "put a break point before deoptimizing") +DEFINE_bool(trap_on_stub_deopt, false, + "put a break point before deoptimizing a stub") DEFINE_bool(deoptimize_uncommon_cases, true, "deoptimize uncommon cases") DEFINE_bool(polymorphic_inlining, true, "polymorphic inlining") DEFINE_bool(use_osr, true, "use on-stack replacement") diff --git a/deps/v8/src/global-handles.cc b/deps/v8/src/global-handles.cc index 2eae474..5df9dd4 100644 --- a/deps/v8/src/global-handles.cc +++ b/deps/v8/src/global-handles.cc @@ -56,9 +56,7 @@ class GlobalHandles::Node { NORMAL, // Normal global handle. WEAK, // Flagged as weak but not yet finalized. PENDING, // Has been recognized as only reachable by weak handles. - NEAR_DEATH, // Callback has informed the handle is near death. - - NUMBER_OF_STATES + NEAR_DEATH // Callback has informed the handle is near death. }; // Maps handle location (slot) to the containing node. @@ -96,12 +94,13 @@ class GlobalHandles::Node { } #endif - void Initialize(int index, Node* first_free) { + void Initialize(int index, Node** first_free) { index_ = static_cast(index); ASSERT(static_cast(index_) == index); set_state(FREE); set_in_new_space_list(false); - parameter_or_next_free_.next_free = first_free; + parameter_or_next_free_.next_free = *first_free; + *first_free = this; } void Acquire(Object* object) { @@ -113,6 +112,7 @@ class GlobalHandles::Node { set_state(NORMAL); parameter_or_next_free_.parameter = NULL; weak_reference_callback_ = NULL; + IncreaseBlockUses(); } void Release() { @@ -126,7 +126,7 @@ class GlobalHandles::Node { set_partially_dependent(false); weak_reference_callback_ = NULL; #endif - ReleaseFromBlock(); + DecreaseBlockUses(); } // Object slot accessors. @@ -205,6 +205,10 @@ class GlobalHandles::Node { } void clear_partially_dependent() { set_partially_dependent(false); } + // Callback accessor. + // TODO(svenpanne) Re-enable or nuke later. + // WeakReferenceCallback callback() { return callback_; } + // Callback parameter accessors. void set_parameter(void* parameter) { ASSERT(state() != FREE); @@ -273,7 +277,8 @@ class GlobalHandles::Node { private: inline NodeBlock* FindBlock(); inline GlobalHandles* GetGlobalHandles(); - inline void ReleaseFromBlock(); + inline void IncreaseBlockUses(); + inline void DecreaseBlockUses(); // Storage for object pointer. // Placed first to avoid offset computation. @@ -311,404 +316,163 @@ class GlobalHandles::Node { }; -class GlobalHandles::BlockListIterator { - public: - explicit inline BlockListIterator(BlockList* anchor) - : anchor_(anchor), current_(anchor->next()) { - ASSERT(anchor->IsAnchor()); - } - inline BlockList* block() const { - ASSERT(!done()); - return current_; - } - inline bool done() const { - ASSERT_EQ(anchor_ == current_, current_->IsAnchor()); - return anchor_ == current_; - } - inline void Advance() { - ASSERT(!done()); - current_ = current_->next(); - } - - private: - BlockList* const anchor_; - BlockList* current_; - DISALLOW_COPY_AND_ASSIGN(BlockListIterator); -}; - - -GlobalHandles::BlockList::BlockList() - : prev_block_(this), - next_block_(this), - first_free_(NULL), - used_nodes_(0) {} - - -void GlobalHandles::BlockList::InsertAsNext(BlockList* const block) { - ASSERT(block != this); - ASSERT(!block->IsAnchor()); - ASSERT(block->IsDetached()); - block->prev_block_ = this; - block->next_block_ = next_block_; - next_block_->prev_block_ = block; - next_block_ = block; - ASSERT(!IsDetached()); - ASSERT(!block->IsDetached()); -} - - -void GlobalHandles::BlockList::Detach() { - ASSERT(!IsAnchor()); - ASSERT(!IsDetached()); - prev_block_->next_block_ = next_block_; - next_block_->prev_block_ = prev_block_; - prev_block_ = this; - next_block_ = this; - ASSERT(IsDetached()); -} - - -bool GlobalHandles::BlockList::HasAtLeastLength(int length) { - ASSERT(IsAnchor()); - ASSERT(length > 0); - for (BlockListIterator it(this); !it.done(); it.Advance()) { - if (--length <= 0) return true; - } - return false; -} - - -#ifdef DEBUG -int GlobalHandles::BlockList::LengthOfFreeList() { - int count = 0; - Node* node = first_free_; - while (node != NULL) { - count++; - node = node->next_free(); - } - return count; -} -#endif - - -int GlobalHandles::BlockList::CompareBlocks(const void* a, const void* b) { - const BlockList* block_a = - *reinterpret_cast(reinterpret_cast(a)); - const BlockList* block_b = - *reinterpret_cast(reinterpret_cast(b)); - if (block_a->used_nodes() > block_b->used_nodes()) return -1; - if (block_a->used_nodes() == block_b->used_nodes()) return 0; - return 1; -} - - -class GlobalHandles::NodeBlock : public BlockList { +class GlobalHandles::NodeBlock { public: static const int kSize = 256; - explicit NodeBlock(GlobalHandles* global_handles) - : global_handles_(global_handles) { - // Initialize nodes - Node* first_free = first_free_; + explicit NodeBlock(GlobalHandles* global_handles, NodeBlock* next) + : next_(next), + used_nodes_(0), + next_used_(NULL), + prev_used_(NULL), + global_handles_(global_handles) {} + + void PutNodesOnFreeList(Node** first_free) { for (int i = kSize - 1; i >= 0; --i) { nodes_[i].Initialize(i, first_free); - first_free = &nodes_[i]; } - first_free_ = first_free; - ASSERT(!IsAnchor()); - // Link into global_handles - ASSERT(global_handles->non_full_blocks_.IsDetached()); - global_handles->non_full_blocks_.InsertAsHead(this); - global_handles->number_of_blocks_++; } - Node* Acquire(Object* o) { + Node* node_at(int index) { + ASSERT(0 <= index && index < kSize); + return &nodes_[index]; + } + + void IncreaseUses() { ASSERT(used_nodes_ < kSize); - ASSERT(first_free_ != NULL); - ASSERT(global_handles_->non_full_blocks_.next() == this); - // Remove from free list - Node* node = first_free_; - first_free_ = node->next_free(); - // Increment counters - global_handles_->isolate()->counters()->global_handles()->Increment(); - global_handles_->number_of_global_handles_++; - // Initialize node with value - node->Acquire(o); - bool now_full = ++used_nodes_ == kSize; - ASSERT_EQ(now_full, first_free_ == NULL); - if (now_full) { - // Move block to tail of non_full_blocks_ - Detach(); - global_handles_->full_blocks_.InsertAsTail(this); + if (used_nodes_++ == 0) { + NodeBlock* old_first = global_handles_->first_used_block_; + global_handles_->first_used_block_ = this; + next_used_ = old_first; + prev_used_ = NULL; + if (old_first == NULL) return; + old_first->prev_used_ = this; } - return node; } - void Release(Node* node) { + void DecreaseUses() { ASSERT(used_nodes_ > 0); - // Add to free list - node->set_next_free(first_free_); - first_free_ = node; - // Decrement counters - global_handles_->isolate()->counters()->global_handles()->Decrement(); - global_handles_->number_of_global_handles_--; - bool was_full = used_nodes_-- == kSize; - ASSERT_EQ(was_full, first_free_->next_free() == NULL); - if (was_full) { - // Move this block to head of non_full_blocks_ - Detach(); - global_handles_->non_full_blocks_.InsertAsHead(this); + if (--used_nodes_ == 0) { + if (next_used_ != NULL) next_used_->prev_used_ = prev_used_; + if (prev_used_ != NULL) prev_used_->next_used_ = next_used_; + if (this == global_handles_->first_used_block_) { + global_handles_->first_used_block_ = next_used_; + } } } - Node* node_at(int index) { - ASSERT(0 <= index && index < kSize); - return &nodes_[index]; - } - GlobalHandles* global_handles() { return global_handles_; } - static NodeBlock* Cast(BlockList* block_list) { - ASSERT(!block_list->IsAnchor()); - return static_cast(block_list); - } + // Next block in the list of all blocks. + NodeBlock* next() const { return next_; } - static NodeBlock* From(Node* node, uint8_t index) { - uintptr_t ptr = reinterpret_cast(node - index); - ptr -= OFFSET_OF(NodeBlock, nodes_); - NodeBlock* block = reinterpret_cast(ptr); - ASSERT(block->node_at(index) == node); - return block; - } + // Next/previous block in the list of blocks with used nodes. + NodeBlock* next_used() const { return next_used_; } + NodeBlock* prev_used() const { return prev_used_; } private: Node nodes_[kSize]; + NodeBlock* const next_; + int used_nodes_; + NodeBlock* next_used_; + NodeBlock* prev_used_; GlobalHandles* global_handles_; }; -void GlobalHandles::BlockList::SortBlocks(GlobalHandles* global_handles, - bool prune) { - // Always sort at least 2 blocks - if (!global_handles->non_full_blocks_.HasAtLeastLength(2)) return; - // build a vector that could contain the upper bound of the block count - int number_of_blocks = global_handles->block_count(); - // Build array of blocks and update number_of_blocks to actual count - ScopedVector blocks(number_of_blocks); - { - int i = 0; - BlockList* anchor = &global_handles->non_full_blocks_; - for (BlockListIterator it(anchor); !it.done(); it.Advance()) { - blocks[i++] = it.block(); - } - number_of_blocks = i; - } - // Nothing to do. - if (number_of_blocks <= 1) return; - // Sort blocks - qsort(blocks.start(), number_of_blocks, sizeof(blocks[0]), CompareBlocks); - // Prune empties - if (prune) { - static const double kUnusedPercentage = 0.30; - static const double kUsedPercentage = 1.30; - int total_slots = global_handles->number_of_blocks_ * NodeBlock::kSize; - const int total_used = global_handles->number_of_global_handles_; - const int target_unused = static_cast(Max( - total_used * kUsedPercentage, - total_slots * kUnusedPercentage)); - // Reverse through empty blocks. Note: always leave one block free. - int blocks_deleted = 0; - for (int i = number_of_blocks - 1; i > 0 && blocks[i]->IsUnused(); i--) { - // Not worth deleting - if (total_slots - total_used < target_unused) break; - blocks[i]->Detach(); - delete blocks[i]; - blocks_deleted++; - total_slots -= NodeBlock::kSize; - } - global_handles->number_of_blocks_ -= blocks_deleted; - number_of_blocks -= blocks_deleted; - } - // Relink all blocks - for (int i = 0; i < number_of_blocks; i++) { - blocks[i]->Detach(); - global_handles->non_full_blocks_.InsertAsTail(blocks[i]); - } -#ifdef DEBUG - // Check sorting - BlockList* anchor = &global_handles->non_full_blocks_; - int last_size = NodeBlock::kSize; - for (BlockListIterator it(anchor); !it.done(); it.Advance()) { - ASSERT(it.block()->used_nodes() <= last_size); - last_size = it.block()->used_nodes(); - } -#endif -} - - -#ifdef DEBUG -void GlobalHandles::VerifyBlockInvariants() { - int number_of_blocks = 0; - int number_of_handles = 0; - for (int i = 0; i < kAllAnchorsSize; i++) { - for (BlockListIterator it(all_anchors_[i]); !it.done(); it.Advance()) { - BlockList* block = it.block(); - number_of_blocks++; - int used_nodes = block->used_nodes(); - number_of_handles += used_nodes; - int unused_nodes = block->LengthOfFreeList(); - ASSERT_EQ(used_nodes + unused_nodes, NodeBlock::kSize); - if (all_anchors_[i] == &full_blocks_) { - ASSERT_EQ(NodeBlock::kSize, used_nodes); - } else { - ASSERT_NE(NodeBlock::kSize, used_nodes); - } - } - } - ASSERT_EQ(number_of_handles, number_of_global_handles_); - ASSERT_EQ(number_of_blocks, number_of_blocks_); -} -#endif - - -void GlobalHandles::SortBlocks(bool shouldPrune) { -#ifdef DEBUG - VerifyBlockInvariants(); -#endif - BlockList::SortBlocks(this, shouldPrune); -#ifdef DEBUG - VerifyBlockInvariants(); -#endif -} - - GlobalHandles* GlobalHandles::Node::GetGlobalHandles() { return FindBlock()->global_handles(); } GlobalHandles::NodeBlock* GlobalHandles::Node::FindBlock() { - return NodeBlock::From(this, index_); + intptr_t ptr = reinterpret_cast(this); + ptr = ptr - index_ * sizeof(Node); + NodeBlock* block = reinterpret_cast(ptr); + ASSERT(block->node_at(index_) == this); + return block; +} + + +void GlobalHandles::Node::IncreaseBlockUses() { + NodeBlock* node_block = FindBlock(); + node_block->IncreaseUses(); + GlobalHandles* global_handles = node_block->global_handles(); + global_handles->isolate()->counters()->global_handles()->Increment(); + global_handles->number_of_global_handles_++; } -void GlobalHandles::Node::ReleaseFromBlock() { - FindBlock()->Release(this); +void GlobalHandles::Node::DecreaseBlockUses() { + NodeBlock* node_block = FindBlock(); + GlobalHandles* global_handles = node_block->global_handles(); + parameter_or_next_free_.next_free = global_handles->first_free_; + global_handles->first_free_ = this; + node_block->DecreaseUses(); + global_handles->isolate()->counters()->global_handles()->Decrement(); + global_handles->number_of_global_handles_--; } class GlobalHandles::NodeIterator { public: explicit NodeIterator(GlobalHandles* global_handles) - : all_anchors_(global_handles->all_anchors_), - block_(all_anchors_[0]), - anchor_index_(0), - node_index_(0) { - AdvanceBlock(); - } + : block_(global_handles->first_used_block_), + index_(0) {} - bool done() const { - return anchor_index_ == kAllAnchorsSize; - } + bool done() const { return block_ == NULL; } Node* node() const { ASSERT(!done()); - return NodeBlock::Cast(block_)->node_at(node_index_); + return block_->node_at(index_); } void Advance() { ASSERT(!done()); - if (++node_index_ < NodeBlock::kSize) return; - node_index_ = 0; - AdvanceBlock(); + if (++index_ < NodeBlock::kSize) return; + index_ = 0; + block_ = block_->next_used(); } - typedef int CountArray[Node::NUMBER_OF_STATES]; - static int CollectStats(GlobalHandles* global_handles, CountArray counts); - private: - void AdvanceBlock() { - ASSERT(!done()); - while (true) { - block_ = block_->next(); - // block is valid - if (block_ != all_anchors_[anchor_index_]) { - ASSERT(!done()); - ASSERT(!block_->IsAnchor()); - // skip empty blocks - if (block_->IsUnused()) continue; - return; - } - // jump lists - anchor_index_++; - if (anchor_index_ == kAllAnchorsSize) break; - block_ = all_anchors_[anchor_index_]; - } - ASSERT(done()); - } - - BlockList* const * const all_anchors_; - BlockList* block_; - int anchor_index_; - int node_index_; + NodeBlock* block_; + int index_; DISALLOW_COPY_AND_ASSIGN(NodeIterator); }; -int GlobalHandles::NodeIterator::CollectStats(GlobalHandles* global_handles, - CountArray counts) { - static const int kSize = Node::NUMBER_OF_STATES; - for (int i = 0; i < kSize; i++) { - counts[i] = 0; - } - int total = 0; - for (NodeIterator it(global_handles); !it.done(); it.Advance()) { - total++; - Node::State state = it.node()->state(); - ASSERT(state >= 0 && state < kSize); - counts[state]++; - } - // NodeIterator skips empty blocks - int skipped = global_handles->number_of_blocks_ * NodeBlock::kSize - total; - total += skipped; - counts[Node::FREE] += total; - return total; -} - - GlobalHandles::GlobalHandles(Isolate* isolate) : isolate_(isolate), - number_of_blocks_(0), number_of_global_handles_(0), + first_block_(NULL), + first_used_block_(NULL), + first_free_(NULL), post_gc_processing_count_(0), - object_group_connections_(kObjectGroupConnectionsCapacity) { - all_anchors_[0] = &full_blocks_; - all_anchors_[1] = &non_full_blocks_; -} + object_group_connections_(kObjectGroupConnectionsCapacity) {} GlobalHandles::~GlobalHandles() { - for (int i = 0; i < kAllAnchorsSize; i++) { - BlockList* block = all_anchors_[i]->next(); - while (block != all_anchors_[i]) { - BlockList* tmp = block->next(); - block->Detach(); - delete NodeBlock::Cast(block); - block = tmp; - } + NodeBlock* block = first_block_; + while (block != NULL) { + NodeBlock* tmp = block->next(); + delete block; + block = tmp; } + first_block_ = NULL; } Handle GlobalHandles::Create(Object* value) { - if (non_full_blocks_.IsDetached()) { - new NodeBlock(this); - ASSERT(!non_full_blocks_.IsDetached()); - } - ASSERT(non_full_blocks_.IsAnchor()); - ASSERT(!non_full_blocks_.next()->IsAnchor()); - Node* result = NodeBlock::Cast(non_full_blocks_.next())->Acquire(value); + if (first_free_ == NULL) { + first_block_ = new NodeBlock(this, first_block_); + first_block_->PutNodesOnFreeList(&first_free_); + } + ASSERT(first_free_ != NULL); + // Take the first node in the free list. + Node* result = first_free_; + first_free_ = result->next_free(); + result->Acquire(value); if (isolate_->heap()->InNewSpace(value) && !result->is_in_new_space_list()) { new_space_nodes_.Add(result); @@ -898,33 +662,22 @@ bool GlobalHandles::PostGarbageCollectionProcessing( } } } else { - // Must cache all blocks, as NodeIterator can't survive mutation. - List blocks(number_of_blocks_); - for (int i = 0; i < kAllAnchorsSize; i++) { - for (BlockListIterator it(all_anchors_[i]); !it.done(); it.Advance()) { - blocks.Add(NodeBlock::Cast(it.block())); + for (NodeIterator it(this); !it.done(); it.Advance()) { + if (!it.node()->IsRetainer()) { + // Free nodes do not have weak callbacks. Do not use them to compute + // the next_gc_likely_to_collect_more. + continue; } - } - for (int block_index = 0; block_index < blocks.length(); block_index++) { - NodeBlock* block = blocks[block_index]; - for (int node_index = 0; node_index < NodeBlock::kSize; node_index++) { - Node* node = block->node_at(node_index); - if (!node->IsRetainer()) { - // Free nodes do not have weak callbacks. Do not use them to compute - // the next_gc_likely_to_collect_more. - continue; - } - node->clear_partially_dependent(); - if (node->PostGarbageCollectionProcessing(isolate_)) { - if (initial_post_gc_processing_count != post_gc_processing_count_) { - // See the comment above. - return next_gc_likely_to_collect_more; - } - } - if (!node->IsRetainer()) { - next_gc_likely_to_collect_more = true; + it.node()->clear_partially_dependent(); + if (it.node()->PostGarbageCollectionProcessing(isolate_)) { + if (initial_post_gc_processing_count != post_gc_processing_count_) { + // See the comment above. + return next_gc_likely_to_collect_more; } } + if (!it.node()->IsRetainer()) { + next_gc_likely_to_collect_more = true; + } } } // Update the list of new space nodes. @@ -946,8 +699,6 @@ bool GlobalHandles::PostGarbageCollectionProcessing( } } new_space_nodes_.Rewind(last); - bool shouldPruneBlocks = collector != SCAVENGER; - SortBlocks(shouldPruneBlocks); return next_gc_likely_to_collect_more; } @@ -1015,30 +766,48 @@ int GlobalHandles::NumberOfGlobalObjectWeakHandles() { void GlobalHandles::RecordStats(HeapStats* stats) { - NodeIterator::CountArray counts; - int total = NodeIterator::CollectStats(this, counts); - *stats->global_handle_count = total; - *stats->weak_global_handle_count = counts[Node::WEAK]; - *stats->pending_global_handle_count = counts[Node::PENDING]; - *stats->near_death_global_handle_count = counts[Node::NEAR_DEATH]; - *stats->free_global_handle_count = counts[Node::FREE]; + *stats->global_handle_count = 0; + *stats->weak_global_handle_count = 0; + *stats->pending_global_handle_count = 0; + *stats->near_death_global_handle_count = 0; + *stats->free_global_handle_count = 0; + for (NodeIterator it(this); !it.done(); it.Advance()) { + *stats->global_handle_count += 1; + if (it.node()->state() == Node::WEAK) { + *stats->weak_global_handle_count += 1; + } else if (it.node()->state() == Node::PENDING) { + *stats->pending_global_handle_count += 1; + } else if (it.node()->state() == Node::NEAR_DEATH) { + *stats->near_death_global_handle_count += 1; + } else if (it.node()->state() == Node::FREE) { + *stats->free_global_handle_count += 1; + } + } } - #ifdef DEBUG void GlobalHandles::PrintStats() { - NodeIterator::CountArray counts; - int total = NodeIterator::CollectStats(this, counts); - size_t total_consumed = sizeof(NodeBlock) * number_of_blocks_; + int total = 0; + int weak = 0; + int pending = 0; + int near_death = 0; + int destroyed = 0; + + for (NodeIterator it(this); !it.done(); it.Advance()) { + total++; + if (it.node()->state() == Node::WEAK) weak++; + if (it.node()->state() == Node::PENDING) pending++; + if (it.node()->state() == Node::NEAR_DEATH) near_death++; + if (it.node()->state() == Node::FREE) destroyed++; + } + PrintF("Global Handle Statistics:\n"); - PrintF(" allocated blocks = %d\n", number_of_blocks_); - PrintF(" allocated memory = %" V8_PTR_PREFIX "dB\n", total_consumed); - PrintF(" # normal = %d\n", counts[Node::NORMAL]); - PrintF(" # weak = %d\n", counts[Node::WEAK]); - PrintF(" # pending = %d\n", counts[Node::PENDING]); - PrintF(" # near_death = %d\n", counts[Node::NEAR_DEATH]); - PrintF(" # free = %d\n", counts[Node::FREE]); + PrintF(" allocated memory = %" V8_PTR_PREFIX "dB\n", sizeof(Node) * total); + PrintF(" # weak = %d\n", weak); + PrintF(" # pending = %d\n", pending); + PrintF(" # near_death = %d\n", near_death); + PrintF(" # free = %d\n", destroyed); PrintF(" # total = %d\n", total); } diff --git a/deps/v8/src/global-handles.h b/deps/v8/src/global-handles.h index 2c20711..5a4ad13 100644 --- a/deps/v8/src/global-handles.h +++ b/deps/v8/src/global-handles.h @@ -157,9 +157,6 @@ class GlobalHandles { return number_of_global_handles_; } - // Returns the current number of allocated blocks - int block_count() const { return number_of_blocks_; } - // Clear the weakness of a global handle. static void ClearWeakness(Object** location); @@ -279,14 +276,11 @@ class GlobalHandles { #ifdef DEBUG void PrintStats(); void Print(); - void VerifyBlockInvariants(); #endif private: explicit GlobalHandles(Isolate* isolate); - void SortBlocks(bool shouldPrune); - // Migrates data from the internal representation (object_group_connections_, // retainer_infos_ and implicit_ref_connections_) to the public and more // efficient representation (object_groups_ and implicit_ref_groups_). @@ -300,64 +294,20 @@ class GlobalHandles { class Node; class NodeBlock; class NodeIterator; - class BlockListIterator; - // Base class for NodeBlock - class BlockList { - public: - BlockList(); - ~BlockList() { ASSERT(IsDetached()); } - void Detach(); - void InsertAsHead(BlockList* block) { - ASSERT(IsAnchor()); - InsertAsNext(block); - } - void InsertAsTail(BlockList* block) { - ASSERT(IsAnchor()); - prev_block_->InsertAsNext(block); - } - inline bool IsAnchor() { return first_free_ == NULL && used_nodes_ == 0; } - inline bool IsDetached() { - ASSERT_EQ(prev_block_ == this, next_block_ == this); - return prev_block_ == this; - } - bool HasAtLeastLength(int length); - bool IsUnused() { return used_nodes_ == 0; } - int used_nodes() const { return used_nodes_; } - BlockList* next() { return next_block_; } - BlockList* prev() { return prev_block_; } -#ifdef DEBUG - int LengthOfFreeList(); -#endif - static void SortBlocks(GlobalHandles* global_handles, bool prune); - - protected: - BlockList* prev_block_; - BlockList* next_block_; - Node* first_free_; - int used_nodes_; - - private: - // Needed for quicksort - static int CompareBlocks(const void* a, const void* b); - void InsertAsNext(BlockList* block); - DISALLOW_COPY_AND_ASSIGN(BlockList); - }; Isolate* isolate_; - // Field always containing the number of blocks allocated. - int number_of_blocks_; // Field always containing the number of handles to global objects. int number_of_global_handles_; - // Anchors for doubly linked lists of blocks - BlockList full_blocks_; - BlockList non_full_blocks_; + // List of all allocated node blocks. + NodeBlock* first_block_; + + // List of node blocks with used nodes. + NodeBlock* first_used_block_; - // An array of all the anchors held by GlobalHandles. - // This simplifies iteration across all blocks. - static const int kAllAnchorsSize = 2; - BlockList* all_anchors_[kAllAnchorsSize]; + // Free list of nodes. + Node* first_free_; // Contains all nodes holding new space objects. Note: when the list // is accessed, some of the objects may have been promoted already. diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc index 53088e2..9d8a6fa 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -703,6 +703,16 @@ bool Heap::CollectGarbage(AllocationSpace space, } +int Heap::NotifyContextDisposed() { + if (FLAG_parallel_recompilation) { + // Flush the queued recompilation tasks. + isolate()->optimizing_compiler_thread()->Flush(); + } + flush_monomorphic_ics_ = true; + return ++contexts_disposed_; +} + + void Heap::PerformScavenge() { GCTracer tracer(this, NULL, NULL); if (incremental_marking()->IsStopped()) { @@ -3253,6 +3263,12 @@ bool Heap::RootCanBeWrittenAfterInitialization(Heap::RootListIndex root_index) { } +bool Heap::RootCanBeTreatedAsConstant(RootListIndex root_index) { + return !RootCanBeWrittenAfterInitialization(root_index) && + !InNewSpace(roots_array_start()[root_index]); +} + + Object* RegExpResultsCache::Lookup(Heap* heap, String* key_string, Object* key_pattern, @@ -4465,7 +4481,8 @@ void Heap::InitializeJSObjectFromMap(JSObject* obj, } -MaybeObject* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) { +MaybeObject* Heap::AllocateJSObjectFromMap( + Map* map, PretenureFlag pretenure, bool allocate_properties) { // JSFunctions should be allocated using AllocateFunction to be // properly initialized. ASSERT(map->instance_type() != JS_FUNCTION_TYPE); @@ -4476,11 +4493,15 @@ MaybeObject* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) { ASSERT(map->instance_type() != JS_BUILTINS_OBJECT_TYPE); // Allocate the backing storage for the properties. - int prop_size = map->InitialPropertiesLength(); - ASSERT(prop_size >= 0); - Object* properties; - { MaybeObject* maybe_properties = AllocateFixedArray(prop_size, pretenure); - if (!maybe_properties->ToObject(&properties)) return maybe_properties; + FixedArray* properties; + if (allocate_properties) { + int prop_size = map->InitialPropertiesLength(); + ASSERT(prop_size >= 0); + { MaybeObject* maybe_properties = AllocateFixedArray(prop_size, pretenure); + if (!maybe_properties->To(&properties)) return maybe_properties; + } + } else { + properties = empty_fixed_array(); } // Allocate the JSObject. @@ -4492,17 +4513,15 @@ MaybeObject* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) { if (!maybe_obj->To(&obj)) return maybe_obj; // Initialize the JSObject. - InitializeJSObjectFromMap(JSObject::cast(obj), - FixedArray::cast(properties), - map); + InitializeJSObjectFromMap(JSObject::cast(obj), properties, map); ASSERT(JSObject::cast(obj)->HasFastElements() || JSObject::cast(obj)->HasExternalArrayElements()); return obj; } -MaybeObject* Heap::AllocateJSObjectFromMapWithAllocationSite(Map* map, - Handle allocation_site) { +MaybeObject* Heap::AllocateJSObjectFromMapWithAllocationSite( + Map* map, Handle allocation_site) { // JSFunctions should be allocated using AllocateFunction to be // properly initialized. ASSERT(map->instance_type() != JS_FUNCTION_TYPE); @@ -4515,9 +4534,9 @@ MaybeObject* Heap::AllocateJSObjectFromMapWithAllocationSite(Map* map, // Allocate the backing storage for the properties. int prop_size = map->InitialPropertiesLength(); ASSERT(prop_size >= 0); - Object* properties; + FixedArray* properties; { MaybeObject* maybe_properties = AllocateFixedArray(prop_size); - if (!maybe_properties->ToObject(&properties)) return maybe_properties; + if (!maybe_properties->To(&properties)) return maybe_properties; } // Allocate the JSObject. @@ -4529,9 +4548,7 @@ MaybeObject* Heap::AllocateJSObjectFromMapWithAllocationSite(Map* map, if (!maybe_obj->To(&obj)) return maybe_obj; // Initialize the JSObject. - InitializeJSObjectFromMap(JSObject::cast(obj), - FixedArray::cast(properties), - map); + InitializeJSObjectFromMap(JSObject::cast(obj), properties, map); ASSERT(JSObject::cast(obj)->HasFastElements()); return obj; } diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h index 1b6bf8e..78c0e5b 100644 --- a/deps/v8/src/heap.h +++ b/deps/v8/src/heap.h @@ -736,7 +736,7 @@ class Heap { // failed. // Please note this does not perform a garbage collection. MUST_USE_RESULT MaybeObject* AllocateJSObjectFromMap( - Map* map, PretenureFlag pretenure = NOT_TENURED); + Map* map, PretenureFlag pretenure = NOT_TENURED, bool alloc_props = true); MUST_USE_RESULT MaybeObject* AllocateJSObjectFromMapWithAllocationSite( Map* map, Handle allocation_site); @@ -1254,10 +1254,7 @@ class Heap { void EnsureHeapIsIterable(); // Notify the heap that a context has been disposed. - int NotifyContextDisposed() { - flush_monomorphic_ics_ = true; - return ++contexts_disposed_; - } + int NotifyContextDisposed(); // Utility to invoke the scavenger. This is needed in test code to // ensure correct callback for weak global handles. @@ -1618,6 +1615,8 @@ class Heap { // Generated code can embed direct references to non-writable roots if // they are in new space. static bool RootCanBeWrittenAfterInitialization(RootListIndex root_index); + // Generated code can treat direct references to this root as constant. + bool RootCanBeTreatedAsConstant(RootListIndex root_index); MUST_USE_RESULT MaybeObject* NumberToString( Object* number, bool check_number_string_cache = true, diff --git a/deps/v8/src/hydrogen-dce.cc b/deps/v8/src/hydrogen-dce.cc index 4ad32d2..0e7253d 100644 --- a/deps/v8/src/hydrogen-dce.cc +++ b/deps/v8/src/hydrogen-dce.cc @@ -118,7 +118,9 @@ void HDeadCodeEliminationPhase::RemoveDeadInstructions() { HPhi* phi = worklist.RemoveLast(); HBasicBlock* block = phi->block(); phi->DeleteAndReplaceWith(NULL); - block->RecordDeletedPhi(phi->merged_index()); + if (phi->HasMergedIndex()) { + block->RecordDeletedPhi(phi->merged_index()); + } } } diff --git a/deps/v8/src/hydrogen-escape-analysis.cc b/deps/v8/src/hydrogen-escape-analysis.cc index 961bb94..0359678 100644 --- a/deps/v8/src/hydrogen-escape-analysis.cc +++ b/deps/v8/src/hydrogen-escape-analysis.cc @@ -63,4 +63,234 @@ void HEscapeAnalysisPhase::CollectCapturedValues() { } +HCapturedObject* HEscapeAnalysisPhase::NewState(HInstruction* previous) { + Zone* zone = graph()->zone(); + HCapturedObject* state = new(zone) HCapturedObject(number_of_values_, zone); + state->InsertAfter(previous); + return state; +} + + +// Create a new state for replacing HAllocate instructions. +HCapturedObject* HEscapeAnalysisPhase::NewStateForAllocation( + HInstruction* previous) { + HConstant* undefined = graph()->GetConstantUndefined(); + HCapturedObject* state = NewState(previous); + for (int index = 0; index < number_of_values_; index++) { + state->SetOperandAt(index, undefined); + } + return state; +} + + +// Create a new state full of phis for loop header entries. +HCapturedObject* HEscapeAnalysisPhase::NewStateForLoopHeader( + HInstruction* previous, HCapturedObject* old_state) { + HBasicBlock* block = previous->block(); + HCapturedObject* state = NewState(previous); + for (int index = 0; index < number_of_values_; index++) { + HValue* operand = old_state->OperandAt(index); + HPhi* phi = NewPhiAndInsert(block, operand, index); + state->SetOperandAt(index, phi); + } + return state; +} + + +// Create a new state by copying an existing one. +HCapturedObject* HEscapeAnalysisPhase::NewStateCopy( + HInstruction* previous, HCapturedObject* old_state) { + HCapturedObject* state = NewState(previous); + for (int index = 0; index < number_of_values_; index++) { + HValue* operand = old_state->OperandAt(index); + state->SetOperandAt(index, operand); + } + return state; +} + + +// Insert a newly created phi into the given block and fill all incoming +// edges with the given value. +HPhi* HEscapeAnalysisPhase::NewPhiAndInsert( + HBasicBlock* block, HValue* incoming_value, int index) { + Zone* zone = graph()->zone(); + HPhi* phi = new(zone) HPhi(HPhi::kInvalidMergedIndex, zone); + for (int i = 0; i < block->predecessors()->length(); i++) { + phi->AddInput(incoming_value); + } + block->AddPhi(phi); + return phi; +} + + +// Performs a forward data-flow analysis of all loads and stores on the +// given captured allocation. This uses a reverse post-order iteration +// over affected basic blocks. All non-escaping instructions are handled +// and replaced during the analysis. +void HEscapeAnalysisPhase::AnalyzeDataFlow(HInstruction* allocate) { + HBasicBlock* allocate_block = allocate->block(); + block_states_.AddBlock(NULL, graph()->blocks()->length(), zone()); + + // Iterate all blocks starting with the allocation block, since the + // allocation cannot dominate blocks that come before. + int start = allocate_block->block_id(); + for (int i = start; i < graph()->blocks()->length(); i++) { + HBasicBlock* block = graph()->blocks()->at(i); + HCapturedObject* state = StateAt(block); + + // Skip blocks that are not dominated by the captured allocation. + if (!allocate_block->Dominates(block) && allocate_block != block) continue; + if (FLAG_trace_escape_analysis) { + PrintF("Analyzing data-flow in B%d\n", block->block_id()); + } + + // Go through all instructions of the current block. + for (HInstructionIterator it(block); !it.Done(); it.Advance()) { + HInstruction* instr = it.Current(); + switch (instr->opcode()) { + case HValue::kAllocate: { + if (instr != allocate) continue; + state = NewStateForAllocation(allocate); + break; + } + case HValue::kLoadNamedField: { + HLoadNamedField* load = HLoadNamedField::cast(instr); + int index = load->access().offset() / kPointerSize; + if (load->object() != allocate) continue; + ASSERT(load->access().IsInobject()); + HValue* replacement = state->OperandAt(index); + load->DeleteAndReplaceWith(replacement); + if (FLAG_trace_escape_analysis) { + PrintF("Replacing load #%d with #%d (%s)\n", instr->id(), + replacement->id(), replacement->Mnemonic()); + } + break; + } + case HValue::kStoreNamedField: { + HStoreNamedField* store = HStoreNamedField::cast(instr); + int index = store->access().offset() / kPointerSize; + if (store->object() != allocate) continue; + ASSERT(store->access().IsInobject()); + state = NewStateCopy(store, state); + state->SetOperandAt(index, store->value()); + if (store->has_transition()) { + state->SetOperandAt(0, store->transition()); + } + store->DeleteAndReplaceWith(NULL); + if (FLAG_trace_escape_analysis) { + PrintF("Replacing store #%d%s\n", instr->id(), + store->has_transition() ? " (with transition)" : ""); + } + break; + } + case HValue::kSimulate: { + HSimulate* simulate = HSimulate::cast(instr); + // TODO(mstarzinger): This doesn't track deltas for values on the + // operand stack yet. Find a repro test case and fix this. + for (int i = 0; i < simulate->OperandCount(); i++) { + if (simulate->OperandAt(i) != allocate) continue; + simulate->SetOperandAt(i, state); + } + break; + } + case HValue::kArgumentsObject: + case HValue::kCapturedObject: { + for (int i = 0; i < instr->OperandCount(); i++) { + if (instr->OperandAt(i) != allocate) continue; + instr->SetOperandAt(i, state); + } + break; + } + case HValue::kCheckHeapObject: { + HCheckHeapObject* check = HCheckHeapObject::cast(instr); + if (check->value() != allocate) continue; + check->DeleteAndReplaceWith(NULL); + break; + } + case HValue::kCheckMaps: { + HCheckMaps* mapcheck = HCheckMaps::cast(instr); + if (mapcheck->value() != allocate) continue; + // TODO(mstarzinger): This approach breaks if the tracked map value + // is not a HConstant. Find a repro test case and fix this. + for (HUseIterator it(mapcheck->uses()); !it.Done(); it.Advance()) { + if (!it.value()->IsLoadNamedField()) continue; + HLoadNamedField* load = HLoadNamedField::cast(it.value()); + ASSERT(load->typecheck() == mapcheck); + load->ClearTypeCheck(); + } + ASSERT(mapcheck->HasNoUses()); + + mapcheck->DeleteAndReplaceWith(NULL); + break; + } + default: + // Nothing to see here, move along ... + break; + } + } + + // Propagate the block state forward to all successor blocks. + for (int i = 0; i < block->end()->SuccessorCount(); i++) { + HBasicBlock* succ = block->end()->SuccessorAt(i); + if (!allocate_block->Dominates(succ)) continue; + if (succ->predecessors()->length() == 1) { + // Case 1: This is the only predecessor, just reuse state. + SetStateAt(succ, state); + } else if (StateAt(succ) == NULL && succ->IsLoopHeader()) { + // Case 2: This is a state that enters a loop header, be + // pessimistic about loop headers, add phis for all values. + SetStateAt(succ, NewStateForLoopHeader(succ->first(), state)); + } else if (StateAt(succ) == NULL) { + // Case 3: This is the first state propagated forward to the + // successor, leave a copy of the current state. + SetStateAt(succ, NewStateCopy(succ->first(), state)); + } else { + // Case 4: This is a state that needs merging with previously + // propagated states, potentially introducing new phis lazily or + // adding values to existing phis. + HCapturedObject* succ_state = StateAt(succ); + for (int index = 0; index < number_of_values_; index++) { + HValue* operand = state->OperandAt(index); + HValue* succ_operand = succ_state->OperandAt(index); + if (succ_operand->IsPhi() && succ_operand->block() == succ) { + // Phi already exists, add operand. + HPhi* phi = HPhi::cast(succ_operand); + phi->SetOperandAt(succ->PredecessorIndexOf(block), operand); + } else if (succ_operand != operand) { + // Phi does not exist, introduce one. + HPhi* phi = NewPhiAndInsert(succ, succ_operand, index); + phi->SetOperandAt(succ->PredecessorIndexOf(block), operand); + succ_state->SetOperandAt(index, phi); + } + } + } + } + } + + // All uses have been handled. + ASSERT(allocate->HasNoUses()); + allocate->DeleteAndReplaceWith(NULL); +} + + +void HEscapeAnalysisPhase::PerformScalarReplacement() { + for (int i = 0; i < captured_.length(); i++) { + HAllocate* allocate = HAllocate::cast(captured_.at(i)); + + // Compute number of scalar values and start with clean slate. + if (!allocate->size()->IsInteger32Constant()) continue; + int size_in_bytes = allocate->size()->GetInteger32Constant(); + number_of_values_ = size_in_bytes / kPointerSize; + block_states_.Clear(); + + // Perform actual analysis steps. + AnalyzeDataFlow(allocate); + + cumulative_values_ += number_of_values_; + ASSERT(allocate->HasNoUses()); + ASSERT(!allocate->IsLinked()); + } +} + + } } // namespace v8::internal diff --git a/deps/v8/src/hydrogen-escape-analysis.h b/deps/v8/src/hydrogen-escape-analysis.h index 6ba6e82..123da21 100644 --- a/deps/v8/src/hydrogen-escape-analysis.h +++ b/deps/v8/src/hydrogen-escape-analysis.h @@ -38,17 +38,48 @@ namespace internal { class HEscapeAnalysisPhase : public HPhase { public: explicit HEscapeAnalysisPhase(HGraph* graph) - : HPhase("H_Escape analysis", graph), captured_(0, zone()) { } + : HPhase("H_Escape analysis", graph), + captured_(0, zone()), + number_of_values_(0), + cumulative_values_(0), + block_states_(graph->blocks()->length(), zone()) { } void Run() { CollectCapturedValues(); + PerformScalarReplacement(); } private: void CollectCapturedValues(); void CollectIfNoEscapingUses(HInstruction* instr); + void PerformScalarReplacement(); + void AnalyzeDataFlow(HInstruction* instr); - ZoneList captured_; + HCapturedObject* NewState(HInstruction* prev); + HCapturedObject* NewStateForAllocation(HInstruction* prev); + HCapturedObject* NewStateForLoopHeader(HInstruction* prev, HCapturedObject*); + HCapturedObject* NewStateCopy(HInstruction* prev, HCapturedObject* state); + + HPhi* NewPhiAndInsert(HBasicBlock* block, HValue* incoming_value, int index); + + HCapturedObject* StateAt(HBasicBlock* block) { + return block_states_.at(block->block_id()); + } + + void SetStateAt(HBasicBlock* block, HCapturedObject* state) { + block_states_.Set(block->block_id(), state); + } + + // List of allocations captured during collection phase. + ZoneList captured_; + + // Number of scalar values tracked during scalar replacement phase. + int number_of_values_; + int cumulative_values_; + + // Map of block IDs to the data-flow state at block entry during the + // scalar replacement phase. + ZoneList block_states_; }; diff --git a/deps/v8/src/hydrogen-gvn.h b/deps/v8/src/hydrogen-gvn.h index 64a0fec..fdbad99 100644 --- a/deps/v8/src/hydrogen-gvn.h +++ b/deps/v8/src/hydrogen-gvn.h @@ -48,7 +48,7 @@ class HGlobalValueNumberingPhase : public HPhase { // instructions during the first pass. if (FLAG_smi_only_arrays && removed_side_effects_) { Analyze(); - ASSERT(!removed_side_effects_); + // TODO(danno): Turn this into a fixpoint iteration. } } diff --git a/deps/v8/src/hydrogen-instructions.cc b/deps/v8/src/hydrogen-instructions.cc index 997b7c2..a4c54e7 100644 --- a/deps/v8/src/hydrogen-instructions.cc +++ b/deps/v8/src/hydrogen-instructions.cc @@ -388,7 +388,7 @@ HUseListNode* HUseListNode::tail() { } -bool HValue::CheckUsesForFlag(Flag f) { +bool HValue::CheckUsesForFlag(Flag f) const { for (HUseIterator it(uses()); !it.Done(); it.Advance()) { if (it.value()->IsSimulate()) continue; if (!it.value()->CheckFlag(f)) return false; @@ -397,7 +397,7 @@ bool HValue::CheckUsesForFlag(Flag f) { } -bool HValue::HasAtLeastOneUseWithFlagAndNoneWithout(Flag f) { +bool HValue::HasAtLeastOneUseWithFlagAndNoneWithout(Flag f) const { bool return_value = false; for (HUseIterator it(uses()); !it.Done(); it.Advance()) { if (it.value()->IsSimulate()) continue; @@ -1285,14 +1285,26 @@ static HValue* SimplifiedDividendForMathFloorOfDiv(HValue* dividend) { HValue* HUnaryMathOperation::Canonicalize() { - if (op() == kMathFloor) { + if (op() == kMathRound || op() == kMathFloor) { HValue* val = value(); if (val->IsChange()) val = HChange::cast(val)->value(); - // If the input is integer32 then we replace the floor instruction - // with its input. - if (val->representation().IsSmiOrInteger32()) return val; + // If the input is smi or integer32 then we replace the instruction with its + // input. + if (val->representation().IsSmiOrInteger32()) { + if (!val->representation().Equals(representation())) { + HChange* result = new(block()->zone()) HChange( + val, representation(), false, false); + result->InsertBefore(this); + return result; + } + return val; + } + } + if (op() == kMathFloor) { + HValue* val = value(); + if (val->IsChange()) val = HChange::cast(val)->value(); if (val->IsDiv() && (val->UseCount() == 1)) { HDiv* hdiv = HDiv::cast(val); HValue* left = hdiv->left(); @@ -1302,7 +1314,7 @@ HValue* HUnaryMathOperation::Canonicalize() { if (new_left == NULL && hdiv->observed_input_representation(1).IsSmiOrInteger32()) { new_left = new(block()->zone()) HChange( - left, Representation::Integer32(), false, false, false); + left, Representation::Integer32(), false, false); HChange::cast(new_left)->InsertBefore(this); } HValue* new_right = @@ -1313,7 +1325,7 @@ HValue* HUnaryMathOperation::Canonicalize() { #endif hdiv->observed_input_representation(2).IsSmiOrInteger32()) { new_right = new(block()->zone()) HChange( - right, Representation::Integer32(), false, false, false); + right, Representation::Integer32(), false, false); HChange::cast(new_right)->InsertBefore(this); } @@ -2773,6 +2785,18 @@ void HCompareObjectEqAndBranch::PrintDataTo(StringStream* stream) { } +void HCompareHoleAndBranch::PrintDataTo(StringStream* stream) { + object()->PrintNameTo(stream); + HControlInstruction::PrintDataTo(stream); +} + + +void HCompareHoleAndBranch::InferRepresentation( + HInferRepresentationPhase* h_infer) { + ChangeRepresentation(object()->representation()); +} + + void HGoto::PrintDataTo(StringStream* stream) { stream->Add("B%d", SuccessorAt(0)->block_id()); } @@ -2832,119 +2856,6 @@ void HLoadNamedField::PrintDataTo(StringStream* stream) { } -// Returns true if an instance of this map can never find a property with this -// name in its prototype chain. This means all prototypes up to the top are -// fast and don't have the name in them. It would be good if we could optimize -// polymorphic loads where the property is sometimes found in the prototype -// chain. -static bool PrototypeChainCanNeverResolve( - Handle map, Handle name) { - Isolate* isolate = map->GetIsolate(); - Object* current = map->prototype(); - while (current != isolate->heap()->null_value()) { - if (current->IsJSGlobalProxy() || - current->IsGlobalObject() || - !current->IsJSObject() || - JSObject::cast(current)->map()->has_named_interceptor() || - JSObject::cast(current)->IsAccessCheckNeeded() || - !JSObject::cast(current)->HasFastProperties()) { - return false; - } - - LookupResult lookup(isolate); - Map* map = JSObject::cast(current)->map(); - map->LookupDescriptor(NULL, *name, &lookup); - if (lookup.IsFound()) return false; - if (!lookup.IsCacheable()) return false; - current = JSObject::cast(current)->GetPrototype(); - } - return true; -} - - -HLoadNamedFieldPolymorphic::HLoadNamedFieldPolymorphic(HValue* context, - HValue* object, - SmallMapList* types, - Handle name, - Zone* zone) - : types_(Min(types->length(), kMaxLoadPolymorphism), zone), - name_(name), - types_unique_ids_(0, zone), - name_unique_id_(), - need_generic_(false) { - SetOperandAt(0, context); - SetOperandAt(1, object); - set_representation(Representation::Tagged()); - SetGVNFlag(kDependsOnMaps); - SmallMapList negative_lookups; - for (int i = 0; - i < types->length() && types_.length() < kMaxLoadPolymorphism; - ++i) { - Handle map = types->at(i); - // Deprecated maps are updated to the current map in the type oracle. - ASSERT(!map->is_deprecated()); - LookupResult lookup(map->GetIsolate()); - map->LookupDescriptor(NULL, *name, &lookup); - if (lookup.IsFound()) { - switch (lookup.type()) { - case FIELD: { - int index = lookup.GetLocalFieldIndexFromMap(*map); - if (index < 0) { - SetGVNFlag(kDependsOnInobjectFields); - } else { - SetGVNFlag(kDependsOnBackingStoreFields); - } - if (FLAG_track_double_fields && - lookup.representation().IsDouble()) { - // Since the value needs to be boxed, use a generic handler for - // loading doubles. - continue; - } - types_.Add(types->at(i), zone); - break; - } - case CONSTANT: - types_.Add(types->at(i), zone); - break; - case CALLBACKS: - break; - case TRANSITION: - case INTERCEPTOR: - case NONEXISTENT: - case NORMAL: - case HANDLER: - UNREACHABLE(); - break; - } - } else if (lookup.IsCacheable() && - // For dicts the lookup on the map will fail, but the object may - // contain the property so we cannot generate a negative lookup - // (which would just be a map check and return undefined). - !map->is_dictionary_map() && - !map->has_named_interceptor() && - PrototypeChainCanNeverResolve(map, name)) { - negative_lookups.Add(types->at(i), zone); - } - } - - bool need_generic = - (types->length() != negative_lookups.length() + types_.length()); - if (!need_generic && FLAG_deoptimize_uncommon_cases) { - SetFlag(kUseGVN); - for (int i = 0; i < negative_lookups.length(); i++) { - types_.Add(negative_lookups.at(i), zone); - } - } else { - // We don't have an easy way to handle both a call (to the generic stub) and - // a deopt in the same hydrogen instruction, so in this case we don't add - // the negative lookups which can deopt - just let the generic stub handle - // them. - SetAllSideEffects(); - need_generic_ = true; - } -} - - HCheckMaps* HCheckMaps::New(Zone* zone, HValue* context, HValue* value, @@ -2952,8 +2863,7 @@ HCheckMaps* HCheckMaps::New(Zone* zone, CompilationInfo* info, HValue* typecheck) { HCheckMaps* check_map = new(zone) HCheckMaps(value, zone, typecheck); - check_map->map_set_.Add(map, zone); - check_map->has_migration_target_ = map->is_migration_target(); + check_map->Add(map, zone); if (map->CanOmitMapChecks() && value->IsConstant() && HConstant::cast(value)->InstanceOf(map)) { @@ -2973,46 +2883,6 @@ void HCheckMaps::FinalizeUniqueValueId() { } -void HLoadNamedFieldPolymorphic::FinalizeUniqueValueId() { - if (!types_unique_ids_.is_empty()) return; - Zone* zone = block()->zone(); - types_unique_ids_.Initialize(types_.length(), zone); - for (int i = 0; i < types_.length(); i++) { - types_unique_ids_.Add(UniqueValueId(types_.at(i)), zone); - } - name_unique_id_ = UniqueValueId(name_); -} - - -bool HLoadNamedFieldPolymorphic::DataEquals(HValue* value) { - ASSERT_EQ(types_.length(), types_unique_ids_.length()); - HLoadNamedFieldPolymorphic* other = HLoadNamedFieldPolymorphic::cast(value); - if (name_unique_id_ != other->name_unique_id_) return false; - if (types_unique_ids_.length() != other->types_unique_ids_.length()) { - return false; - } - if (need_generic_ != other->need_generic_) return false; - for (int i = 0; i < types_unique_ids_.length(); i++) { - bool found = false; - for (int j = 0; j < types_unique_ids_.length(); j++) { - if (types_unique_ids_.at(j) == other->types_unique_ids_.at(i)) { - found = true; - break; - } - } - if (!found) return false; - } - return true; -} - - -void HLoadNamedFieldPolymorphic::PrintDataTo(StringStream* stream) { - object()->PrintNameTo(stream); - stream->Add("."); - stream->Add(*String::cast(*name())->ToCString()); -} - - void HLoadNamedGeneric::PrintDataTo(StringStream* stream) { object()->PrintNameTo(stream); stream->Add("."); @@ -3085,18 +2955,8 @@ bool HLoadKeyed::UsesMustHandleHole() const { bool HLoadKeyed::AllUsesCanTreatHoleAsNaN() const { - if (!IsFastDoubleElementsKind(elements_kind())) { - return false; - } - - for (HUseIterator it(uses()); !it.Done(); it.Advance()) { - HValue* use = it.value(); - if (!use->CheckFlag(HValue::kAllowUndefinedAsNaN)) { - return false; - } - } - - return true; + return IsFastDoubleElementsKind(elements_kind()) && + CheckUsesForFlag(HValue::kAllowUndefinedAsNaN); } @@ -3313,7 +3173,9 @@ Representation HUnaryMathOperation::RepresentationFromInputs() { // If any of the actual input representation is more general than what we // have so far but not Tagged, use that representation instead. Representation input_rep = value()->representation(); - if (!input_rep.IsTagged()) rep = rep.generalize(input_rep); + if (!input_rep.IsTagged()) { + rep = rep.generalize(input_rep); + } return rep; } @@ -3869,10 +3731,10 @@ void HPhi::SimplifyConstantInputs() { DoubleToInt32(operand->DoubleValue())); integer_input->InsertAfter(operand); SetOperandAt(i, integer_input); - } else if (operand == graph->GetConstantTrue()) { - SetOperandAt(i, graph->GetConstant1()); - } else { - // This catches |false|, |undefined|, strings and objects. + } else if (operand->HasBooleanValue()) { + SetOperandAt(i, operand->BooleanValue() ? graph->GetConstant1() + : graph->GetConstant0()); + } else if (operand->ImmortalImmovable()) { SetOperandAt(i, graph->GetConstant0()); } } diff --git a/deps/v8/src/hydrogen-instructions.h b/deps/v8/src/hydrogen-instructions.h index 3fae45b..41f9d0d 100644 --- a/deps/v8/src/hydrogen-instructions.h +++ b/deps/v8/src/hydrogen-instructions.h @@ -86,6 +86,7 @@ class LChunkBuilder; V(CallNewArray) \ V(CallRuntime) \ V(CallStub) \ + V(CapturedObject) \ V(Change) \ V(CheckFunction) \ V(CheckHeapObject) \ @@ -96,6 +97,7 @@ class LChunkBuilder; V(ClampToUint8) \ V(ClassOfTestAndBranch) \ V(CompareNumericAndBranch) \ + V(CompareHoleAndBranch) \ V(CompareGeneric) \ V(CompareObjectEqAndBranch) \ V(CompareMap) \ @@ -141,7 +143,6 @@ class LChunkBuilder; V(LoadKeyed) \ V(LoadKeyedGeneric) \ V(LoadNamedField) \ - V(LoadNamedFieldPolymorphic) \ V(LoadNamedGeneric) \ V(MapEnumLength) \ V(MathFloorOfDiv) \ @@ -801,10 +802,10 @@ class HValue: public ZoneObject { bool CheckFlag(Flag f) const { return (flags_ & (1 << f)) != 0; } // Returns true if the flag specified is set for all uses, false otherwise. - bool CheckUsesForFlag(Flag f); + bool CheckUsesForFlag(Flag f) const; // Returns true if the flag specified is set for all uses, and this set // of uses is non-empty. - bool HasAtLeastOneUseWithFlagAndNoneWithout(Flag f); + bool HasAtLeastOneUseWithFlagAndNoneWithout(Flag f) const; GVNFlagSet gvn_flags() const { return gvn_flags_; } void SetGVNFlag(GVNFlag f) { gvn_flags_.Add(f); } @@ -1247,19 +1248,23 @@ class HDummyUse: public HTemplateInstruction<1> { class HDeoptimize: public HTemplateInstruction<0> { public: - DECLARE_INSTRUCTION_FACTORY_P1(HDeoptimize, Deoptimizer::BailoutType); + DECLARE_INSTRUCTION_FACTORY_P2(HDeoptimize, const char*, + Deoptimizer::BailoutType); virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } + const char* reason() const { return reason_; } Deoptimizer::BailoutType type() { return type_; } DECLARE_CONCRETE_INSTRUCTION(Deoptimize) private: - explicit HDeoptimize(Deoptimizer::BailoutType type) : type_(type) {} + explicit HDeoptimize(const char* reason, Deoptimizer::BailoutType type) + : reason_(reason), type_(type) {} + const char* reason_; Deoptimizer::BailoutType type_; }; @@ -1517,15 +1522,13 @@ class HChange: public HUnaryOperation { HChange(HValue* value, Representation to, bool is_truncating_to_smi, - bool is_truncating_to_int32, - bool allow_undefined_as_nan) + bool is_truncating_to_int32) : HUnaryOperation(value) { ASSERT(!value->representation().IsNone()); ASSERT(!to.IsNone()); ASSERT(!value->representation().Equals(to)); set_representation(to); SetFlag(kUseGVN); - if (allow_undefined_as_nan) SetFlag(kAllowUndefinedAsNaN); if (is_truncating_to_smi) SetFlag(kTruncatingToSmi); if (is_truncating_to_int32) SetFlag(kTruncatingToInt32); if (value->representation().IsSmi() || value->type().IsSmi()) { @@ -1536,15 +1539,16 @@ class HChange: public HUnaryOperation { } } + bool can_convert_undefined_to_nan() { + return CheckUsesForFlag(kAllowUndefinedAsNaN); + } + virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited); virtual HType CalculateInferredType(); virtual HValue* Canonicalize(); Representation from() const { return value()->representation(); } Representation to() const { return representation(); } - bool allow_undefined_as_nan() const { - return CheckFlag(kAllowUndefinedAsNaN); - } bool deoptimize_on_minus_zero() const { return CheckFlag(kBailoutOnMinusZero); } @@ -2453,7 +2457,6 @@ class HUnaryMathOperation: public HTemplateInstruction<2> { switch (op) { case kMathFloor: case kMathRound: - // TODO(verwaest): Set representation to flexible int starting as smi. set_representation(Representation::Integer32()); break; case kMathAbs: @@ -2531,8 +2534,7 @@ class HCheckMaps: public HTemplateInstruction<2> { HValue *typecheck = NULL) { HCheckMaps* check_map = new(zone) HCheckMaps(value, zone, typecheck); for (int i = 0; i < maps->length(); i++) { - check_map->map_set_.Add(maps->at(i), zone); - check_map->has_migration_target_ |= maps->at(i)->is_migration_target(); + check_map->Add(maps->at(i), zone); } check_map->map_set_.Sort(); return check_map; @@ -2576,6 +2578,14 @@ class HCheckMaps: public HTemplateInstruction<2> { } private: + void Add(Handle map, Zone* zone) { + map_set_.Add(map, zone); + if (!has_migration_target_ && map->is_migration_target()) { + has_migration_target_ = true; + SetGVNFlag(kChangesNewSpacePromotion); + } + } + // Clients should use one of the static New* methods above. HCheckMaps(HValue* value, Zone *zone, HValue* typecheck) : HTemplateInstruction<2>(value->type()), @@ -2760,6 +2770,7 @@ class HCheckHeapObject: public HUnaryOperation { public: DECLARE_INSTRUCTION_FACTORY_P1(HCheckHeapObject, HValue*); + virtual bool HasEscapingOperandAt(int index) { return false; } virtual Representation RequiredInputRepresentation(int index) { return Representation::Tagged(); } @@ -3018,7 +3029,7 @@ class HPhi: public HValue { non_phi_uses_[i] = 0; indirect_uses_[i] = 0; } - ASSERT(merged_index >= 0); + ASSERT(merged_index >= 0 || merged_index == kInvalidMergedIndex); SetFlag(kFlexibleRepresentation); SetFlag(kAllowUndefinedAsNaN); } @@ -3041,6 +3052,7 @@ class HPhi: public HValue { bool HasRealUses(); bool IsReceiver() const { return merged_index_ == 0; } + bool HasMergedIndex() const { return merged_index_ != kInvalidMergedIndex; } int merged_index() const { return merged_index_; } @@ -3103,6 +3115,9 @@ class HPhi: public HValue { void SimplifyConstantInputs(); + // Marker value representing an invalid merge index. + static const int kInvalidMergedIndex = -1; + protected: virtual void DeleteFromGraph(); virtual void InternalSetOperandAt(int index, HValue* value) { @@ -3123,21 +3138,10 @@ class HPhi: public HValue { }; -class HArgumentsObject: public HTemplateInstruction<0> { +// Common base class for HArgumentsObject and HCapturedObject. +class HDematerializedObject: public HTemplateInstruction<0> { public: - static HArgumentsObject* New(Zone* zone, - HValue* context, - int count) { - return new(zone) HArgumentsObject(count, zone); - } - - const ZoneList* arguments_values() const { return &values_; } - int arguments_count() const { return values_.length(); } - - void AddArgument(HValue* argument, Zone* zone) { - values_.Add(NULL, zone); // Resize list. - SetOperandAt(values_.length() - 1, argument); - } + HDematerializedObject(int count, Zone* zone) : values_(count, zone) {} virtual int OperandCount() { return values_.length(); } virtual HValue* OperandAt(int index) const { return values_[index]; } @@ -3147,22 +3151,61 @@ class HArgumentsObject: public HTemplateInstruction<0> { return Representation::None(); } - DECLARE_CONCRETE_INSTRUCTION(ArgumentsObject) - protected: virtual void InternalSetOperandAt(int index, HValue* value) { values_[index] = value; } + // List of values tracked by this marker. + ZoneList values_; + private: - HArgumentsObject(int count, Zone* zone) : values_(count, zone) { + virtual bool IsDeletable() const { return true; } +}; + + +class HArgumentsObject: public HDematerializedObject { + public: + static HArgumentsObject* New(Zone* zone, HValue* context, int count) { + return new(zone) HArgumentsObject(count, zone); + } + + // The values contain a list of all elements in the arguments object + // including the receiver object, which is skipped when materializing. + const ZoneList* arguments_values() const { return &values_; } + int arguments_count() const { return values_.length(); } + + void AddArgument(HValue* argument, Zone* zone) { + values_.Add(NULL, zone); // Resize list. + SetOperandAt(values_.length() - 1, argument); + } + + DECLARE_CONCRETE_INSTRUCTION(ArgumentsObject) + + private: + HArgumentsObject(int count, Zone* zone) + : HDematerializedObject(count, zone) { set_representation(Representation::Tagged()); SetFlag(kIsArguments); } +}; - virtual bool IsDeletable() const { return true; } - ZoneList values_; +class HCapturedObject: public HDematerializedObject { + public: + HCapturedObject(int length, Zone* zone) + : HDematerializedObject(length, zone) { + set_representation(Representation::Tagged()); + values_.AddBlock(NULL, length, zone); // Resize list. + } + + // The values contain a list of all in-object properties inside the + // captured object and is index by field index. Properties in the + // properties or elements backing store are not tracked here. + const ZoneList* values() const { return &values_; } + int length() const { return values_.length(); } + + DECLARE_CONCRETE_INSTRUCTION(CapturedObject) }; @@ -3187,8 +3230,9 @@ class HConstant: public HTemplateInstruction<0> { } bool InstanceOf(Handle map) { - return handle_->IsJSObject() && - Handle::cast(handle_)->map() == *map; + Handle constant_object = handle(); + return constant_object->IsJSObject() && + Handle::cast(constant_object)->map() == *map; } bool IsSpecialDouble() const { @@ -3299,6 +3343,7 @@ class HConstant: public HTemplateInstruction<0> { return external_reference_value_; } + bool HasBooleanValue() const { return type_.IsBoolean(); } bool BooleanValue() const { return boolean_value_; } virtual intptr_t Hashcode() { @@ -3915,6 +3960,32 @@ class HCompareNumericAndBranch: public HTemplateControlInstruction<2, 2> { }; +class HCompareHoleAndBranch: public HTemplateControlInstruction<2, 1> { + public: + // TODO(danno): make this private when the IfBuilder properly constructs + // control flow instructions. + explicit HCompareHoleAndBranch(HValue* object) { + SetFlag(kFlexibleRepresentation); + SetFlag(kAllowUndefinedAsNaN); + SetOperandAt(0, object); + } + + DECLARE_INSTRUCTION_FACTORY_P1(HCompareHoleAndBranch, HValue*); + + HValue* object() { return OperandAt(0); } + + virtual void InferRepresentation(HInferRepresentationPhase* h_infer); + + virtual Representation RequiredInputRepresentation(int index) { + return representation(); + } + + virtual void PrintDataTo(StringStream* stream); + + DECLARE_CONCRETE_INSTRUCTION(CompareHoleAndBranch) +}; + + class HCompareObjectEqAndBranch: public HTemplateControlInstruction<2, 2> { public: // TODO(danno): make this private when the IfBuilder properly constructs @@ -4298,6 +4369,11 @@ class HAdd: public HArithmeticBinaryOperation { } } + virtual void RepresentationChanged(Representation to) { + if (to.IsTagged()) ClearFlag(kAllowUndefinedAsNaN); + HArithmeticBinaryOperation::RepresentationChanged(to); + } + DECLARE_CONCRETE_INSTRUCTION(Add) protected: @@ -5512,6 +5588,7 @@ class HLoadNamedField: public HTemplateInstruction<2> { } bool HasTypeCheck() const { return OperandAt(0) != OperandAt(1); } + void ClearTypeCheck() { SetOperandAt(1, object()); } HObjectAccess access() const { return access_; } Representation field_representation() const { return access_.representation(); @@ -5569,45 +5646,6 @@ class HLoadNamedField: public HTemplateInstruction<2> { }; -class HLoadNamedFieldPolymorphic: public HTemplateInstruction<2> { - public: - HLoadNamedFieldPolymorphic(HValue* context, - HValue* object, - SmallMapList* types, - Handle name, - Zone* zone); - - HValue* context() { return OperandAt(0); } - HValue* object() { return OperandAt(1); } - SmallMapList* types() { return &types_; } - Handle name() { return name_; } - bool need_generic() { return need_generic_; } - - virtual Representation RequiredInputRepresentation(int index) { - return Representation::Tagged(); - } - - virtual void PrintDataTo(StringStream* stream); - - DECLARE_CONCRETE_INSTRUCTION(LoadNamedFieldPolymorphic) - - static const int kMaxLoadPolymorphism = 4; - - virtual void FinalizeUniqueValueId(); - - protected: - virtual bool DataEquals(HValue* value); - - private: - SmallMapList types_; - Handle name_; - ZoneList types_unique_ids_; - UniqueValueId name_unique_id_; - bool need_generic_; -}; - - - class HLoadNamedGeneric: public HTemplateInstruction<2> { public: HLoadNamedGeneric(HValue* context, HValue* object, Handle name) @@ -6007,7 +6045,6 @@ class HStoreKeyed DECLARE_INSTRUCTION_FACTORY_P4(HStoreKeyed, HValue*, HValue*, HValue*, ElementsKind); - virtual bool HasEscapingOperandAt(int index) { return index != 0; } virtual Representation RequiredInputRepresentation(int index) { // kind_fast: tagged[int32] = tagged // kind_double: tagged[int32] = double @@ -6526,8 +6563,11 @@ class HToFastProperties: public HUnaryOperation { private: explicit HToFastProperties(HValue* value) : HUnaryOperation(value) { - // This instruction is not marked as having side effects, but - // changes the map of the input operand. Use it only when creating + set_representation(Representation::Tagged()); + SetGVNFlag(kChangesNewSpacePromotion); + + // This instruction is not marked as kChangesMaps, but does + // change the map of the input operand. Use it only when creating // object literals via a runtime call. ASSERT(value->IsCallRuntime()); #ifdef DEBUG @@ -6535,7 +6575,6 @@ class HToFastProperties: public HUnaryOperation { ASSERT(function->function_id == Runtime::kCreateObjectLiteral || function->function_id == Runtime::kCreateObjectLiteralShallow); #endif - set_representation(Representation::Tagged()); } virtual bool IsDeletable() const { return true; } diff --git a/deps/v8/src/hydrogen-mark-deoptimize.cc b/deps/v8/src/hydrogen-mark-deoptimize.cc index 111fcd2..c0236e9 100644 --- a/deps/v8/src/hydrogen-mark-deoptimize.cc +++ b/deps/v8/src/hydrogen-mark-deoptimize.cc @@ -34,14 +34,9 @@ void HMarkDeoptimizeOnUndefinedPhase::Run() { const ZoneList* phi_list = graph()->phi_list(); for (int i = 0; i < phi_list->length(); i++) { HPhi* phi = phi_list->at(i); - if (phi->CheckFlag(HValue::kAllowUndefinedAsNaN)) { - for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) { - HValue* use_value = it.value(); - if (!use_value->CheckFlag(HValue::kAllowUndefinedAsNaN)) { - ProcessPhi(phi); - break; - } - } + if (phi->CheckFlag(HValue::kAllowUndefinedAsNaN) && + !phi->CheckUsesForFlag(HValue::kAllowUndefinedAsNaN)) { + ProcessPhi(phi); } } } @@ -68,4 +63,22 @@ void HMarkDeoptimizeOnUndefinedPhase::ProcessPhi(HPhi* phi) { } } + +void HComputeChangeUndefinedToNaN::Run() { + const ZoneList* blocks(graph()->blocks()); + for (int i = 0; i < blocks->length(); ++i) { + const HBasicBlock* block(blocks->at(i)); + for (HInstruction* current = block->first(); current != NULL; ) { + HInstruction* next = current->next(); + if (current->IsChange()) { + if (HChange::cast(current)->can_convert_undefined_to_nan()) { + current->SetFlag(HValue::kAllowUndefinedAsNaN); + } + } + current = next; + } + } +} + + } } // namespace v8::internal diff --git a/deps/v8/src/hydrogen-mark-deoptimize.h b/deps/v8/src/hydrogen-mark-deoptimize.h index 0aa2c2c..30f35b3 100644 --- a/deps/v8/src/hydrogen-mark-deoptimize.h +++ b/deps/v8/src/hydrogen-mark-deoptimize.h @@ -58,6 +58,18 @@ class HMarkDeoptimizeOnUndefinedPhase : public HPhase { }; +class HComputeChangeUndefinedToNaN : public HPhase { + public: + explicit HComputeChangeUndefinedToNaN(HGraph* graph) + : HPhase("H_Compute change undefined to nan", graph) {} + + void Run(); + + private: + DISALLOW_COPY_AND_ASSIGN(HComputeChangeUndefinedToNaN); +}; + + } } // namespace v8::internal #endif // V8_HYDROGEN_MARK_DEOPTIMIZE_H_ diff --git a/deps/v8/src/hydrogen-osr.cc b/deps/v8/src/hydrogen-osr.cc index 6c3d6ae..73fa40a 100644 --- a/deps/v8/src/hydrogen-osr.cc +++ b/deps/v8/src/hydrogen-osr.cc @@ -117,6 +117,7 @@ void HOsrBuilder::FinishOsrValues() { const ZoneList* phis = osr_loop_entry_->phis(); for (int j = 0; j < phis->length(); j++) { HPhi* phi = phis->at(j); + ASSERT(phi->HasMergedIndex()); osr_values_->at(phi->merged_index())->set_incoming_value(phi); } } diff --git a/deps/v8/src/hydrogen-representation-changes.cc b/deps/v8/src/hydrogen-representation-changes.cc index 63b7b4d..862457d 100644 --- a/deps/v8/src/hydrogen-representation-changes.cc +++ b/deps/v8/src/hydrogen-representation-changes.cc @@ -47,8 +47,6 @@ void HRepresentationChangesPhase::InsertRepresentationChangeForUse( HInstruction* new_value = NULL; bool is_truncating_to_smi = use_value->CheckFlag(HValue::kTruncatingToSmi); bool is_truncating_to_int = use_value->CheckFlag(HValue::kTruncatingToInt32); - bool allow_undefined_as_nan = - use_value->CheckFlag(HValue::kAllowUndefinedAsNaN); if (value->IsConstant()) { HConstant* constant = HConstant::cast(value); // Try to create a new copy of the constant with the new representation. @@ -61,10 +59,8 @@ void HRepresentationChangesPhase::InsertRepresentationChangeForUse( } if (new_value == NULL) { - new_value = new(graph()->zone()) HChange(value, to, - is_truncating_to_smi, - is_truncating_to_int, - allow_undefined_as_nan); + new_value = new(graph()->zone()) HChange( + value, to, is_truncating_to_smi, is_truncating_to_int); } new_value->InsertBefore(next); @@ -127,7 +123,7 @@ void HRepresentationChangesPhase::Run() { !(input_representation.IsInteger32() && use->CheckFlag(HValue::kTruncatingToInt32))) || (phi->representation().IsSmi() && - !(input_representation.IsSmi() || + !(input_representation.IsSmi() && use->CheckFlag(HValue::kTruncatingToSmi)))) { if (FLAG_trace_representation) { PrintF("#%d Phi is not truncating because of #%d %s\n", diff --git a/deps/v8/src/hydrogen.cc b/deps/v8/src/hydrogen.cc index 837c978..ba1de7a 100644 --- a/deps/v8/src/hydrogen.cc +++ b/deps/v8/src/hydrogen.cc @@ -148,6 +148,16 @@ void HBasicBlock::AddInstruction(HInstruction* instr) { } +HPhi* HBasicBlock::AddNewPhi(int merged_index) { + if (graph()->IsInsideNoSideEffectsScope()) { + merged_index = HPhi::kInvalidMergedIndex; + } + HPhi* phi = new(zone()) HPhi(merged_index, zone()); + AddPhi(phi); + return phi; +} + + HSimulate* HBasicBlock::CreateSimulate(BailoutId ast_id, RemovableSimulate removable) { ASSERT(HasEnvironment()); @@ -203,7 +213,7 @@ void HBasicBlock::Goto(HBasicBlock* block, UpdateEnvironment(last_environment()->DiscardInlined(drop_extra)); } - if (add_simulate) AddSimulate(BailoutId::None()); + if (add_simulate) AddNewSimulate(BailoutId::None()); HGoto* instr = new(zone()) HGoto(block); Finish(instr); } @@ -219,7 +229,7 @@ void HBasicBlock::AddLeaveInlined(HValue* return_value, AddInstruction(new(zone()) HLeaveInlined()); UpdateEnvironment(last_environment()->DiscardInlined(drop_extra)); last_environment()->Push(return_value); - AddSimulate(BailoutId::None()); + AddNewSimulate(BailoutId::None()); HGoto* instr = new(zone()) HGoto(target); Finish(instr); } @@ -824,14 +834,14 @@ void HGraphBuilder::IfBuilder::Else() { } -void HGraphBuilder::IfBuilder::Deopt() { +void HGraphBuilder::IfBuilder::Deopt(const char* reason) { ASSERT(did_then_); if (did_else_) { deopt_else_ = true; } else { deopt_then_ = true; } - builder_->Add(Deoptimizer::EAGER); + builder_->Add(reason, Deoptimizer::EAGER); } @@ -904,8 +914,7 @@ HValue* HGraphBuilder::LoopBuilder::BeginBody( HValue* terminating, Token::Value token) { HEnvironment* env = builder_->environment(); - phi_ = new(zone()) HPhi(env->values()->length(), zone()); - header_block_->AddPhi(phi_); + phi_ = header_block_->AddNewPhi(env->values()->length()); phi_->AddInput(initial); env->Push(initial); builder_->current_block()->GotoNoSimulate(header_block_); @@ -982,7 +991,7 @@ HGraph* HGraphBuilder::CreateGraph() { HInstruction* HGraphBuilder::AddInstruction(HInstruction* instr) { ASSERT(current_block() != NULL); current_block()->AddInstruction(instr); - if (no_side_effects_scope_count_ > 0) { + if (graph()->IsInsideNoSideEffectsScope()) { instr->SetFlag(HValue::kHasNoObservableSideEffects); } return instr; @@ -1006,8 +1015,8 @@ void HGraphBuilder::AddIncrementCounter(StatsCounter* counter, void HGraphBuilder::AddSimulate(BailoutId id, RemovableSimulate removable) { ASSERT(current_block() != NULL); - ASSERT(no_side_effects_scope_count_ == 0); - current_block()->AddSimulate(id, removable); + ASSERT(!graph()->IsInsideNoSideEffectsScope()); + current_block()->AddNewSimulate(id, removable); } @@ -1034,10 +1043,10 @@ HValue* HGraphBuilder::BuildCheckHeapObject(HValue* obj) { void HGraphBuilder::FinishExitWithHardDeoptimization( - HBasicBlock* continuation) { + const char* reason, HBasicBlock* continuation) { PadEnvironmentForContinuation(current_block(), continuation); - Add(Deoptimizer::EAGER); - if (no_side_effects_scope_count_ > 0) { + Add(reason, Deoptimizer::EAGER); + if (graph()->IsInsideNoSideEffectsScope()) { current_block()->GotoNoSimulate(continuation); } else { current_block()->Goto(continuation); @@ -1108,7 +1117,7 @@ HValue* HGraphBuilder::BuildCheckForCapacityGrow(HValue* object, IfBuilder key_checker(this); key_checker.If(key, max_capacity, Token::LT); key_checker.Then(); - key_checker.ElseDeopt(); + key_checker.ElseDeopt("Key out of capacity range"); key_checker.End(); HValue* new_capacity = BuildNewElementsCapacity(key); @@ -1182,7 +1191,7 @@ void HGraphBuilder::BuildTransitionElementsKind(HValue* object, } if (!IsSimpleMapChangeTransition(from_kind, to_kind)) { - HInstruction* elements = AddLoadElements(object); + HInstruction* elements = AddLoadElements(object, NULL); HInstruction* empty_fixed_array = Add( isolate()->factory()->empty_fixed_array()); @@ -1264,7 +1273,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( negative_checker.Then(); HInstruction* result = AddExternalArrayElementAccess( external_elements, key, val, bounds_check, elements_kind, is_store); - negative_checker.ElseDeopt(); + negative_checker.ElseDeopt("Negative key encountered"); length_checker.End(); return result; } else { @@ -1626,13 +1635,26 @@ void HGraphBuilder::BuildCopyElements(HValue* from_elements, from_elements_kind, ALLOW_RETURN_HOLE); - ElementsKind holey_kind = IsFastSmiElementsKind(to_elements_kind) + ElementsKind kind = (IsHoleyElementsKind(from_elements_kind) && + IsFastSmiElementsKind(to_elements_kind)) ? FAST_HOLEY_ELEMENTS : to_elements_kind; - HInstruction* holey_store = Add(to_elements, key, - element, holey_kind); - // Allow NaN hole values to converted to their tagged counterparts. - if (IsFastHoleyElementsKind(to_elements_kind)) { - holey_store->SetFlag(HValue::kAllowUndefinedAsNaN); + + if (IsHoleyElementsKind(from_elements_kind) && + from_elements_kind != to_elements_kind) { + IfBuilder if_hole(this); + if_hole.If(element); + if_hole.Then(); + HConstant* hole_constant = IsFastDoubleElementsKind(to_elements_kind) + ? Add(FixedDoubleArray::hole_nan_as_double()) + : graph()->GetConstantHole(); + Add(to_elements, key, hole_constant, kind); + if_hole.Else(); + HStoreKeyed* store = Add(to_elements, key, element, kind); + store->SetFlag(HValue::kAllowUndefinedAsNaN); + if_hole.End(); + } else { + HStoreKeyed* store = Add(to_elements, key, element, kind); + store->SetFlag(HValue::kAllowUndefinedAsNaN); } builder.EndBody(); @@ -1691,7 +1713,7 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HValue* boilerplate, if (length > 0) { // Get hold of the elements array of the boilerplate and setup the // elements pointer in the resulting object. - HValue* boilerplate_elements = AddLoadElements(boilerplate); + HValue* boilerplate_elements = AddLoadElements(boilerplate, NULL); HValue* object_elements = Add(object, elems_offset); Add(object, HObjectAccess::ForElementsPointer(), object_elements); @@ -1751,7 +1773,7 @@ void HGraphBuilder::BuildCompareNil( // emitted below is the actual monomorphic map. BuildCheckMap(value, type->Classes().Current()); } else { - if_nil.Deopt(); + if_nil.Deopt("Too many undetectable types"); } } @@ -1824,7 +1846,7 @@ HValue* HGraphBuilder::JSArrayBuilder::EmitMapCode() { // map, because we can just load the map in that case. HObjectAccess access = HObjectAccess::ForPrototypeOrInitialMap(); return builder()->AddInstruction( - builder()->BuildLoadNamedField(constructor_function_, access)); + builder()->BuildLoadNamedField(constructor_function_, access, NULL)); } HInstruction* native_context = builder()->BuildGetNativeContext(); @@ -1845,7 +1867,7 @@ HValue* HGraphBuilder::JSArrayBuilder::EmitInternalMapCode() { // Find the map near the constructor function HObjectAccess access = HObjectAccess::ForPrototypeOrInitialMap(); return builder()->AddInstruction( - builder()->BuildLoadNamedField(constructor_function_, access)); + builder()->BuildLoadNamedField(constructor_function_, access, NULL)); } @@ -2049,7 +2071,8 @@ HGraph::HGraph(CompilationInfo* info) has_soft_deoptimize_(false), depends_on_empty_array_proto_elements_(false), type_change_checksum_(0), - maximum_environment_size_(0) { + maximum_environment_size_(0), + no_side_effects_scope_count_(0) { if (info->IsStub()) { HydrogenCodeStub* stub = info->code_stub(); CodeStubInterfaceDescriptor* descriptor = @@ -2916,6 +2939,9 @@ bool HGraph::Optimize(BailoutReason* bailout_reason) { // Remove dead code and phis if (FLAG_dead_code_elimination) Run(); + + if (FLAG_use_escape_analysis) Run(); + CollectPhis(); if (has_osr()) osr()->FinishOsrValues(); @@ -2939,12 +2965,11 @@ bool HGraph::Optimize(BailoutReason* bailout_reason) { if (FLAG_use_canonicalizing) Run(); - if (FLAG_use_escape_analysis) Run(); - if (FLAG_use_gvn) Run(); if (FLAG_use_range) Run(); + Run(); Run(); // Eliminate redundant stack checks on backwards branches. @@ -3346,7 +3371,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { if (stmt->switch_type() == SwitchStatement::SMI_SWITCH) { if (!clause->compare_type()->Is(Type::Smi())) { - Add(Deoptimizer::SOFT); + Add("Non-smi switch type", Deoptimizer::SOFT); } HCompareNumericAndBranch* compare_ = @@ -4287,6 +4312,7 @@ void HOptimizedGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { int data_size = 0; int pointer_size = 0; int max_properties = kMaxFastLiteralProperties; + HCheckMaps* type_check = NULL; if (IsFastLiteral(original_boilerplate_object, kMaxFastLiteralDepth, &max_properties, @@ -4324,7 +4350,7 @@ void HOptimizedGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { // De-opt if elements kind changed from boilerplate_elements_kind. Handle map = Handle(original_boilerplate_object->map(), isolate()); - Add(literal, map, top_info()); + type_check = Add(literal, map, top_info()); } // The array is expected in the bailout environment during computation @@ -4345,7 +4371,7 @@ void HOptimizedGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { HValue* value = Pop(); if (!Smi::IsValid(i)) return Bailout(kNonSmiKeyInArrayLiteral); - elements = AddLoadElements(literal); + elements = AddLoadElements(literal, type_check); HValue* key = Add(i); @@ -4399,9 +4425,10 @@ static bool ComputeLoadStoreField(Handle type, } -void HOptimizedGraphBuilder::AddCheckMap(HValue* object, Handle map) { +HCheckMaps* HOptimizedGraphBuilder::AddCheckMap(HValue* object, + Handle map) { BuildCheckHeapObject(object); - Add(object, map, top_info()); + return Add(object, map, top_info()); } @@ -4567,8 +4594,8 @@ HInstruction* HOptimizedGraphBuilder::TryLoadPolymorphicAsMonomorphic( if (count == types->length()) { // Everything matched; can use monomorphic load. BuildCheckHeapObject(object); - Add(object, types); - return BuildLoadNamedField(object, access); + HCheckMaps* type_check = Add(object, types); + return BuildLoadNamedField(object, access, type_check); } if (count != 0) return NULL; @@ -4589,14 +4616,44 @@ HInstruction* HOptimizedGraphBuilder::TryLoadPolymorphicAsMonomorphic( if (!lookup.IsField()) return NULL; BuildCheckHeapObject(object); - Add(object, types); + HCheckMaps* type_check = Add(object, types); Handle holder(lookup.holder()); Handle holder_map(holder->map()); BuildCheckPrototypeMaps(Handle::cast(prototype), holder); HValue* holder_value = Add(holder); return BuildLoadNamedField(holder_value, - HObjectAccess::ForField(holder_map, &lookup, name)); + HObjectAccess::ForField(holder_map, &lookup, name), type_check); +} + + +// Returns true if an instance of this map can never find a property with this +// name in its prototype chain. This means all prototypes up to the top are +// fast and don't have the name in them. It would be good if we could optimize +// polymorphic loads where the property is sometimes found in the prototype +// chain. +static bool PrototypeChainCanNeverResolve( + Handle map, Handle name) { + Isolate* isolate = map->GetIsolate(); + Object* current = map->prototype(); + while (current != isolate->heap()->null_value()) { + if (current->IsJSGlobalProxy() || + current->IsGlobalObject() || + !current->IsJSObject() || + JSObject::cast(current)->map()->has_named_interceptor() || + JSObject::cast(current)->IsAccessCheckNeeded() || + !JSObject::cast(current)->HasFastProperties()) { + return false; + } + + LookupResult lookup(isolate); + Map* map = JSObject::cast(current)->map(); + map->LookupDescriptor(NULL, *name, &lookup); + if (lookup.IsFound()) return false; + if (!lookup.IsCacheable()) return false; + current = JSObject::cast(current)->GetPrototype(); + } + return true; } @@ -4607,16 +4664,90 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField( Handle name) { HInstruction* instr = TryLoadPolymorphicAsMonomorphic( expr, object, types, name); - if (instr == NULL) { - // Something did not match; must use a polymorphic load. - BuildCheckHeapObject(object); - HValue* context = environment()->context(); - instr = new(zone()) HLoadNamedFieldPolymorphic( - context, object, types, name, zone()); + if (instr != NULL) { + instr->set_position(expr->position()); + return ast_context()->ReturnInstruction(instr, expr->id()); } - instr->set_position(expr->position()); - return ast_context()->ReturnInstruction(instr, expr->id()); + // Something did not match; must use a polymorphic load. + int count = 0; + HBasicBlock* join = NULL; + for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) { + Handle map = types->at(i); + LookupResult lookup(isolate()); + if (ComputeLoadStoreField(map, name, &lookup, false) || + (lookup.IsCacheable() && + !map->is_dictionary_map() && + !map->has_named_interceptor() && + (lookup.IsConstant() || + (!lookup.IsFound() && + PrototypeChainCanNeverResolve(map, name))))) { + if (count == 0) { + BuildCheckHeapObject(object); + join = graph()->CreateBasicBlock(); + } + ++count; + HBasicBlock* if_true = graph()->CreateBasicBlock(); + HBasicBlock* if_false = graph()->CreateBasicBlock(); + HCompareMap* compare = + new(zone()) HCompareMap(object, map, if_true, if_false); + current_block()->Finish(compare); + + set_current_block(if_true); + + // TODO(verwaest): Merge logic with BuildLoadNamedMonomorphic. + if (lookup.IsField()) { + HObjectAccess access = HObjectAccess::ForField(map, &lookup, name); + HLoadNamedField* load = BuildLoadNamedField(object, access, compare); + load->set_position(expr->position()); + AddInstruction(load); + if (!ast_context()->IsEffect()) Push(load); + } else if (lookup.IsConstant()) { + Handle constant(lookup.GetConstantFromMap(*map), isolate()); + HConstant* hconstant = Add(constant); + if (!ast_context()->IsEffect()) Push(hconstant); + } else { + ASSERT(!lookup.IsFound()); + if (map->prototype()->IsJSObject()) { + Handle prototype(JSObject::cast(map->prototype())); + Handle holder = prototype; + while (holder->map()->prototype()->IsJSObject()) { + holder = handle(JSObject::cast(holder->map()->prototype())); + } + BuildCheckPrototypeMaps(prototype, holder); + } + if (!ast_context()->IsEffect()) Push(graph()->GetConstantUndefined()); + } + + current_block()->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 load", join); + } else { + HInstruction* load = BuildLoadNamedGeneric(object, name, expr); + load->set_position(expr->position()); + AddInstruction(load); + if (!ast_context()->IsEffect()) Push(load); + + if (join != NULL) { + current_block()->Goto(join); + } else { + Add(expr->id(), REMOVABLE_SIMULATE); + if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop()); + return; + } + } + + ASSERT(join != NULL); + join->SetJoinId(expr->id()); + set_current_block(join); + if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop()); } @@ -4735,7 +4866,7 @@ void HOptimizedGraphBuilder::HandlePolymorphicStoreNamedField( // 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(join); + FinishExitWithHardDeoptimization("Unknown map in polymorphic store", join); } else { HInstruction* instr = BuildStoreNamedGeneric(object, name, store_value); instr->set_position(position); @@ -4783,7 +4914,10 @@ void HOptimizedGraphBuilder::HandlePropertyAssignment(Assignment* expr) { HValue* value = environment()->ExpressionStackAt(0); HValue* object = environment()->ExpressionStackAt(1); - if (expr->IsUninitialized()) Add(Deoptimizer::SOFT); + if (expr->IsUninitialized()) { + Add("Insufficient type feedback for property assignment", + Deoptimizer::SOFT); + } return BuildStoreNamed(expr, expr->id(), expr->position(), expr->AssignmentId(), prop, object, value, value); } else { @@ -4829,7 +4963,8 @@ void HOptimizedGraphBuilder::HandleGlobalVariableAssignment( } builder.Then(); builder.Else(); - Add(Deoptimizer::EAGER); + Add("Constant global variable assignment", + Deoptimizer::EAGER); builder.End(); } HInstruction* instr = @@ -5038,7 +5173,7 @@ void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) { Add(operation->id(), REMOVABLE_SIMULATE); } - return BuildStoreNamed(prop, expr->id(), expr->position(), + return BuildStoreNamed(expr, expr->id(), expr->position(), expr->AssignmentId(), prop, object, instr, instr); } else { // Keyed property. @@ -5270,7 +5405,8 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedGeneric( Handle name, Property* expr) { if (expr->IsUninitialized()) { - Add(Deoptimizer::SOFT); + Add("Insufficient feedback for generic named load", + Deoptimizer::SOFT); } HValue* context = environment()->context(); return new(zone()) HLoadNamedGeneric(context, object, name); @@ -5299,18 +5435,18 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic( // Handle access to various length properties if (name->Equals(isolate()->heap()->length_string())) { if (map->instance_type() == JS_ARRAY_TYPE) { - AddCheckMap(object, map); + HCheckMaps* type_check = AddCheckMap(object, map); return New(object, - HObjectAccess::ForArrayLength(map->elements_kind())); + HObjectAccess::ForArrayLength(map->elements_kind()), type_check); } } LookupResult lookup(isolate()); map->LookupDescriptor(NULL, *name, &lookup); if (lookup.IsField()) { - AddCheckMap(object, map); + HCheckMaps* type_check = AddCheckMap(object, map); return BuildLoadNamedField(object, - HObjectAccess::ForField(map, &lookup, name)); + HObjectAccess::ForField(map, &lookup, name), type_check); } // Handle a load of a constant known function. @@ -5326,11 +5462,11 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic( Handle prototype(JSObject::cast(map->prototype())); Handle holder(lookup.holder()); Handle holder_map(holder->map()); - AddCheckMap(object, map); + HCheckMaps* type_check = AddCheckMap(object, map); BuildCheckPrototypeMaps(prototype, holder); HValue* holder_value = Add(holder); return BuildLoadNamedField(holder_value, - HObjectAccess::ForField(holder_map, &lookup, name)); + HObjectAccess::ForField(holder_map, &lookup, name), type_check); } // Handle a load of a constant function somewhere in the prototype chain. @@ -5581,13 +5717,15 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( if (!is_store) { Push(access); } + NoObservableSideEffectsScope scope(this); current_block()->GotoNoSimulate(join); set_current_block(other_map); } // Deopt if none of the cases matched. NoObservableSideEffectsScope scope(this); - FinishExitWithHardDeoptimization(join); + FinishExitWithHardDeoptimization("Unknown type in polymorphic element access", + join); set_current_block(join); return is_store ? NULL : Pop(); } @@ -5623,12 +5761,14 @@ HValue* HOptimizedGraphBuilder::HandleKeyedElementAccess( } else { if (is_store) { if (expr->IsAssignment() && expr->AsAssignment()->IsUninitialized()) { - Add(Deoptimizer::SOFT); + Add("Insufficient feedback for keyed store", + Deoptimizer::SOFT); } instr = BuildStoreKeyedGeneric(obj, key, val); } else { if (expr->AsProperty()->IsUninitialized()) { - Add(Deoptimizer::SOFT); + Add("Insufficient feedback for keyed load", + Deoptimizer::SOFT); } instr = BuildLoadKeyedGeneric(obj, key); } @@ -6075,7 +6215,7 @@ void HOptimizedGraphBuilder::HandlePolymorphicCallNamed( // that the environment stack matches the depth on deopt that it otherwise // would have had after a successful call. Drop(argument_count - (ast_context()->IsEffect() ? 0 : 1)); - FinishExitWithHardDeoptimization(join); + FinishExitWithHardDeoptimization("Unknown map in polymorphic call", join); } else { HValue* context = environment()->context(); HCallNamed* call = new(zone()) HCallNamed(context, name, argument_count); @@ -7473,7 +7613,7 @@ void HOptimizedGraphBuilder::VisitCountOperation(CountOperation* expr) { after = BuildIncrement(returns_original_input, expr); HValue* result = returns_original_input ? Pop() : after; - return BuildStoreNamed(prop, expr->id(), expr->position(), + return BuildStoreNamed(expr, expr->id(), expr->position(), expr->AssignmentId(), prop, object, after, result); } else { // Keyed property. @@ -7635,12 +7775,14 @@ HInstruction* HOptimizedGraphBuilder::BuildBinaryOperation( } if (left_type->Is(Type::None())) { - Add(Deoptimizer::SOFT); + Add("Insufficient type feedback for left side", + Deoptimizer::SOFT); // TODO(rossberg): we should be able to get rid of non-continuous defaults. left_type = handle(Type::Any(), isolate()); } if (right_type->Is(Type::None())) { - Add(Deoptimizer::SOFT); + Add("Insufficient type feedback for right side", + Deoptimizer::SOFT); right_type = handle(Type::Any(), isolate()); } HInstruction* instr = NULL; @@ -7990,7 +8132,8 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) { // Cases handled below depend on collected type feedback. They should // soft deoptimize when there is no type feedback. if (combined_type->Is(Type::None())) { - Add(Deoptimizer::SOFT); + Add("insufficient type feedback for combined type", + Deoptimizer::SOFT); combined_type = left_type = right_type = handle(Type::Any(), isolate()); } @@ -8040,10 +8183,6 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) { result->set_position(expr->position()); return ast_context()->ReturnInstruction(result, expr->id()); } else { - // TODO(verwaest): Remove once Representation::FromType properly - // returns Smi when the IC measures Smi. - if (left_type->Is(Type::Smi())) left_rep = Representation::Smi(); - if (right_type->Is(Type::Smi())) right_rep = Representation::Smi(); HCompareNumericAndBranch* result = new(zone()) HCompareNumericAndBranch(left, right, op); result->set_observed_input_representation(left_rep, right_rep); @@ -9214,20 +9353,19 @@ void HEnvironment::AddIncomingEdge(HBasicBlock* block, HEnvironment* other) { // There is already a phi for the i'th value. HPhi* phi = HPhi::cast(value); // Assert index is correct and that we haven't missed an incoming edge. - ASSERT(phi->merged_index() == i); + ASSERT(phi->merged_index() == i || !phi->HasMergedIndex()); ASSERT(phi->OperandCount() == block->predecessors()->length()); phi->AddInput(other->values_[i]); } else if (values_[i] != other->values_[i]) { // There is a fresh value on the incoming edge, a phi is needed. ASSERT(values_[i] != NULL && other->values_[i] != NULL); - HPhi* phi = new(zone()) HPhi(i, zone()); + HPhi* phi = block->AddNewPhi(i); HValue* old_value = values_[i]; for (int j = 0; j < block->predecessors()->length(); j++) { phi->AddInput(old_value); } phi->AddInput(other->values_[i]); this->values_[i] = phi; - block->AddPhi(phi); } } } @@ -9288,10 +9426,9 @@ HEnvironment* HEnvironment::CopyWithoutHistory() const { HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const { HEnvironment* new_env = Copy(); for (int i = 0; i < values_.length(); ++i) { - HPhi* phi = new(zone()) HPhi(i, zone()); + HPhi* phi = loop_header->AddNewPhi(i); phi->AddInput(values_[i]); new_env->values_[i] = phi; - loop_header->AddPhi(phi); } new_env->ClearHistory(); return new_env; diff --git a/deps/v8/src/hydrogen.h b/deps/v8/src/hydrogen.h index 6312a52..004aa16 100644 --- a/deps/v8/src/hydrogen.h +++ b/deps/v8/src/hydrogen.h @@ -142,8 +142,9 @@ class HBasicBlock: public ZoneObject { } int PredecessorIndexOf(HBasicBlock* predecessor) const; - HSimulate* AddSimulate(BailoutId ast_id, - RemovableSimulate removable = FIXED_SIMULATE) { + HPhi* AddNewPhi(int merged_index); + HSimulate* AddNewSimulate(BailoutId ast_id, + RemovableSimulate removable = FIXED_SIMULATE) { HSimulate* instr = CreateSimulate(ast_id, removable); AddInstruction(instr); return instr; @@ -453,6 +454,10 @@ class HGraph: public ZoneObject { uint32_instructions_->Add(instr, zone()); } + void IncrementInNoSideEffectsScope() { no_side_effects_scope_count_++; } + void DecrementInNoSideEffectsScope() { no_side_effects_scope_count_--; } + bool IsInsideNoSideEffectsScope() { return no_side_effects_scope_count_ > 0; } + private: HConstant* GetConstant(SetOncePointer* pointer, int32_t integer_value); @@ -498,6 +503,7 @@ class HGraph: public ZoneObject { bool depends_on_empty_array_proto_elements_; int type_change_checksum_; int maximum_environment_size_; + int no_side_effects_scope_count_; DISALLOW_COPY_AND_ASSIGN(HGraph); }; @@ -970,8 +976,7 @@ class HGraphBuilder { explicit HGraphBuilder(CompilationInfo* info) : info_(info), graph_(NULL), - current_block_(NULL), - no_side_effects_scope_count_(0) {} + current_block_(NULL) {} virtual ~HGraphBuilder() {} HBasicBlock* current_block() const { return current_block_; } @@ -1186,16 +1191,7 @@ class HGraphBuilder { AddInstruction(NewUncasted(p1, p2, p3, p4, p5, p6, p7, p8))); } - void AddSimulate(BailoutId id, - RemovableSimulate removable = FIXED_SIMULATE); - - void IncrementInNoSideEffectsScope() { - no_side_effects_scope_count_++; - } - - void DecrementInNoSideEffectsScope() { - no_side_effects_scope_count_--; - } + void AddSimulate(BailoutId id, RemovableSimulate removable = FIXED_SIMULATE); protected: virtual bool BuildGraph() = 0; @@ -1258,10 +1254,10 @@ class HGraphBuilder { HLoadNamedField* BuildLoadNamedField( HValue* object, HObjectAccess access, - HValue* typecheck = NULL); - HInstruction* BuildLoadStringLength(HValue* object, HValue* typecheck = NULL); + HValue* typecheck); + HInstruction* BuildLoadStringLength(HValue* object, HValue* typecheck); HStoreNamedField* AddStoreMapConstant(HValue *object, Handle); - HLoadNamedField* AddLoadElements(HValue *object, HValue *typecheck = NULL); + HLoadNamedField* AddLoadElements(HValue *object, HValue *typecheck); HLoadNamedField* AddLoadFixedArrayLength(HValue *object); HValue* AddLoadJSBuiltin(Builtins::JavaScript builtin); @@ -1270,7 +1266,8 @@ class HGraphBuilder { void PushAndAdd(HInstruction* instr); - void FinishExitWithHardDeoptimization(HBasicBlock* continuation); + void FinishExitWithHardDeoptimization(const char* reason, + HBasicBlock* continuation); void AddIncrementCounter(StatsCounter* counter, HValue* context); @@ -1374,10 +1371,10 @@ class HGraphBuilder { void Else(); void End(); - void Deopt(); - void ElseDeopt() { + void Deopt(const char* reason); + void ElseDeopt(const char* reason) { Else(); - Deopt(); + Deopt(reason); } void Return(HValue* value); @@ -1441,20 +1438,6 @@ class HGraphBuilder { bool finished_; }; - class NoObservableSideEffectsScope { - public: - explicit NoObservableSideEffectsScope(HGraphBuilder* builder) : - builder_(builder) { - builder_->IncrementInNoSideEffectsScope(); - } - ~NoObservableSideEffectsScope() { - builder_->DecrementInNoSideEffectsScope(); - } - - private: - HGraphBuilder* builder_; - }; - HValue* BuildNewElementsCapacity(HValue* old_capacity); void BuildNewSpaceArrayCheck(HValue* length, @@ -1576,19 +1559,18 @@ class HGraphBuilder { CompilationInfo* info_; HGraph* graph_; HBasicBlock* current_block_; - int no_side_effects_scope_count_; }; template<> inline HInstruction* HGraphBuilder::AddUncasted( - Deoptimizer::BailoutType type) { + const char* reason, Deoptimizer::BailoutType type) { if (type == Deoptimizer::SOFT) { isolate()->counters()->soft_deopts_requested()->Increment(); if (FLAG_always_opt) return NULL; } if (current_block()->IsDeoptimizing()) return NULL; - HDeoptimize* instr = New(type); + HDeoptimize* instr = New(reason, type); AddInstruction(instr); if (type == Deoptimizer::SOFT) { isolate()->counters()->soft_deopts_inserted()->Increment(); @@ -1601,8 +1583,8 @@ inline HInstruction* HGraphBuilder::AddUncasted( template<> inline HDeoptimize* HGraphBuilder::Add( - Deoptimizer::BailoutType type) { - return static_cast(AddUncasted(type)); + const char* reason, Deoptimizer::BailoutType type) { + return static_cast(AddUncasted(reason, type)); } @@ -2054,7 +2036,7 @@ class HOptimizedGraphBuilder: public HGraphBuilder, public AstVisitor { Property* expr, Handle map); - void AddCheckMap(HValue* object, Handle map); + HCheckMaps* AddCheckMap(HValue* object, Handle map); void BuildStoreNamed(Expression* expression, BailoutId id, @@ -2329,6 +2311,21 @@ class HTracer: public Malloced { }; +class NoObservableSideEffectsScope { + public: + explicit NoObservableSideEffectsScope(HGraphBuilder* builder) : + builder_(builder) { + builder_->graph()->IncrementInNoSideEffectsScope(); + } + ~NoObservableSideEffectsScope() { + builder_->graph()->DecrementInNoSideEffectsScope(); + } + + private: + HGraphBuilder* builder_; +}; + + } } // namespace v8::internal #endif // V8_HYDROGEN_H_ diff --git a/deps/v8/src/i18n.cc b/deps/v8/src/i18n.cc index b2ccfd4..5cfe4c4 100644 --- a/deps/v8/src/i18n.cc +++ b/deps/v8/src/i18n.cc @@ -29,33 +29,85 @@ #include "i18n.h" #include "unicode/calendar.h" +#include "unicode/coll.h" +#include "unicode/curramt.h" +#include "unicode/dcfmtsym.h" +#include "unicode/decimfmt.h" #include "unicode/dtfmtsym.h" #include "unicode/dtptngen.h" #include "unicode/locid.h" +#include "unicode/numfmt.h" #include "unicode/numsys.h" #include "unicode/smpdtfmt.h" #include "unicode/timezone.h" +#include "unicode/uchar.h" +#include "unicode/ucol.h" +#include "unicode/ucurr.h" +#include "unicode/unum.h" +#include "unicode/uversion.h" namespace v8 { namespace internal { namespace { +bool ExtractStringSetting(Isolate* isolate, + Handle options, + const char* key, + icu::UnicodeString* setting) { + Handle str = isolate->factory()->NewStringFromAscii(CStrVector(key)); + MaybeObject* maybe_object = options->GetProperty(*str); + Object* object; + if (maybe_object->ToObject(&object) && object->IsString()) { + v8::String::Utf8Value utf8_string( + v8::Utils::ToLocal(Handle(String::cast(object)))); + *setting = icu::UnicodeString::fromUTF8(*utf8_string); + return true; + } + return false; +} + + +bool ExtractIntegerSetting(Isolate* isolate, + Handle options, + const char* key, + int32_t* value) { + Handle str = isolate->factory()->NewStringFromAscii(CStrVector(key)); + MaybeObject* maybe_object = options->GetProperty(*str); + Object* object; + if (maybe_object->ToObject(&object) && object->IsNumber()) { + object->ToInt32(value); + return true; + } + return false; +} + + +bool ExtractBooleanSetting(Isolate* isolate, + Handle options, + const char* key, + bool* value) { + Handle str = isolate->factory()->NewStringFromAscii(CStrVector(key)); + MaybeObject* maybe_object = options->GetProperty(*str); + Object* object; + if (maybe_object->ToObject(&object) && object->IsBoolean()) { + *value = object->BooleanValue(); + return true; + } + return false; +} + + icu::SimpleDateFormat* CreateICUDateFormat( Isolate* isolate, const icu::Locale& icu_locale, - Handle options) { + Handle options) { // Create time zone as specified by the user. We have to re-create time zone // since calendar takes ownership. icu::TimeZone* tz = NULL; - MaybeObject* maybe_object = options->GetProperty( - *isolate->factory()->NewStringFromAscii(CStrVector("timeZone"))); - Object* timezone; - if (maybe_object->ToObject(&timezone) && timezone->IsString()) { - v8::String::Utf8Value utf8_timezone( - v8::Utils::ToLocal(Handle(String::cast(timezone)))); - icu::UnicodeString u_timezone(icu::UnicodeString::fromUTF8(*utf8_timezone)); - tz = icu::TimeZone::createTimeZone(u_timezone); + icu::UnicodeString timezone; + if (ExtractStringSetting(isolate, options, "timeZone", &timezone)) { + tz = icu::TimeZone::createTimeZone(timezone); } else { tz = icu::TimeZone::createDefault(); } @@ -68,18 +120,13 @@ icu::SimpleDateFormat* CreateICUDateFormat( // Make formatter from skeleton. Calendar and numbering system are added // to the locale as Unicode extension (if they were specified at all). icu::SimpleDateFormat* date_format = NULL; - Object* skeleton; - maybe_object = options->GetProperty( - *isolate->factory()->NewStringFromAscii(CStrVector("skeleton"))); - if (maybe_object->ToObject(&skeleton) && skeleton->IsString()) { - v8::String::Utf8Value utf8_skeleton( - v8::Utils::ToLocal(Handle(String::cast(skeleton)))); - icu::UnicodeString u_skeleton(icu::UnicodeString::fromUTF8(*utf8_skeleton)); + icu::UnicodeString skeleton; + if (ExtractStringSetting(isolate, options, "skeleton", &skeleton)) { icu::DateTimePatternGenerator* generator = icu::DateTimePatternGenerator::createInstance(icu_locale, status); icu::UnicodeString pattern; if (U_SUCCESS(status)) { - pattern = generator->getBestPattern(u_skeleton, status); + pattern = generator->getBestPattern(skeleton, status); delete generator; } @@ -99,10 +146,10 @@ icu::SimpleDateFormat* CreateICUDateFormat( } -void SetResolvedSettings(Isolate* isolate, - const icu::Locale& icu_locale, - icu::SimpleDateFormat* date_format, - Handle resolved) { +void SetResolvedDateSettings(Isolate* isolate, + const icu::Locale& icu_locale, + icu::SimpleDateFormat* date_format, + Handle resolved) { UErrorCode status = U_ZERO_ERROR; icu::UnicodeString pattern; date_format->toPattern(pattern); @@ -217,6 +264,473 @@ Handle GetEternal(Isolate* isolate) { field)); } + +icu::DecimalFormat* CreateICUNumberFormat( + Isolate* isolate, + const icu::Locale& icu_locale, + Handle options) { + // Make formatter from options. Numbering system is added + // to the locale as Unicode extension (if it was specified at all). + UErrorCode status = U_ZERO_ERROR; + icu::DecimalFormat* number_format = NULL; + icu::UnicodeString style; + icu::UnicodeString currency; + if (ExtractStringSetting(isolate, options, "style", &style)) { + if (style == UNICODE_STRING_SIMPLE("currency")) { + icu::UnicodeString display; + ExtractStringSetting(isolate, options, "currency", ¤cy); + ExtractStringSetting(isolate, options, "currencyDisplay", &display); + +#if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6) + icu::NumberFormat::EStyles format_style; + if (display == UNICODE_STRING_SIMPLE("code")) { + format_style = icu::NumberFormat::kIsoCurrencyStyle; + } else if (display == UNICODE_STRING_SIMPLE("name")) { + format_style = icu::NumberFormat::kPluralCurrencyStyle; + } else { + format_style = icu::NumberFormat::kCurrencyStyle; + } +#else // ICU version is 4.8 or above (we ignore versions below 4.0). + UNumberFormatStyle format_style; + if (display == UNICODE_STRING_SIMPLE("code")) { + format_style = UNUM_CURRENCY_ISO; + } else if (display == UNICODE_STRING_SIMPLE("name")) { + format_style = UNUM_CURRENCY_PLURAL; + } else { + format_style = UNUM_CURRENCY; + } +#endif + + number_format = static_cast( + icu::NumberFormat::createInstance(icu_locale, format_style, status)); + } else if (style == UNICODE_STRING_SIMPLE("percent")) { + number_format = static_cast( + icu::NumberFormat::createPercentInstance(icu_locale, status)); + if (U_FAILURE(status)) { + delete number_format; + return NULL; + } + // Make sure 1.1% doesn't go into 2%. + number_format->setMinimumFractionDigits(1); + } else { + // Make a decimal instance by default. + number_format = static_cast( + icu::NumberFormat::createInstance(icu_locale, status)); + } + } + + if (U_FAILURE(status)) { + delete number_format; + return NULL; + } + + // Set all options. + if (!currency.isEmpty()) { + number_format->setCurrency(currency.getBuffer(), status); + } + + int32_t digits; + if (ExtractIntegerSetting( + isolate, options, "minimumIntegerDigits", &digits)) { + number_format->setMinimumIntegerDigits(digits); + } + + if (ExtractIntegerSetting( + isolate, options, "minimumFractionDigits", &digits)) { + number_format->setMinimumFractionDigits(digits); + } + + if (ExtractIntegerSetting( + isolate, options, "maximumFractionDigits", &digits)) { + number_format->setMaximumFractionDigits(digits); + } + + bool significant_digits_used = false; + if (ExtractIntegerSetting( + isolate, options, "minimumSignificantDigits", &digits)) { + number_format->setMinimumSignificantDigits(digits); + significant_digits_used = true; + } + + if (ExtractIntegerSetting( + isolate, options, "maximumSignificantDigits", &digits)) { + number_format->setMaximumSignificantDigits(digits); + significant_digits_used = true; + } + + number_format->setSignificantDigitsUsed(significant_digits_used); + + bool grouping; + if (ExtractBooleanSetting(isolate, options, "useGrouping", &grouping)) { + number_format->setGroupingUsed(grouping); + } + + // Set rounding mode. + number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp); + + return number_format; +} + + +void SetResolvedNumberSettings(Isolate* isolate, + const icu::Locale& icu_locale, + icu::DecimalFormat* number_format, + Handle resolved) { + icu::UnicodeString pattern; + number_format->toPattern(pattern); + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("pattern")), + isolate->factory()->NewStringFromTwoByte( + Vector( + reinterpret_cast(pattern.getBuffer()), + pattern.length())), + NONE, + kNonStrictMode); + + // Set resolved currency code in options.currency if not empty. + icu::UnicodeString currency(number_format->getCurrency()); + if (!currency.isEmpty()) { + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("currency")), + isolate->factory()->NewStringFromTwoByte( + Vector( + reinterpret_cast(currency.getBuffer()), + currency.length())), + NONE, + kNonStrictMode); + } + + // Ugly hack. ICU doesn't expose numbering system in any way, so we have + // to assume that for given locale NumberingSystem constructor produces the + // same digits as NumberFormat/Calendar would. + UErrorCode status = U_ZERO_ERROR; + icu::NumberingSystem* numbering_system = + icu::NumberingSystem::createInstance(icu_locale, status); + if (U_SUCCESS(status)) { + const char* ns = numbering_system->getName(); + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("numberingSystem")), + isolate->factory()->NewStringFromAscii(CStrVector(ns)), + NONE, + kNonStrictMode); + } else { + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("numberingSystem")), + isolate->factory()->undefined_value(), + NONE, + kNonStrictMode); + } + delete numbering_system; + + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("useGrouping")), + isolate->factory()->ToBoolean(number_format->isGroupingUsed()), + NONE, + kNonStrictMode); + + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii( + CStrVector("minimumIntegerDigits")), + isolate->factory()->NewNumberFromInt( + number_format->getMinimumIntegerDigits()), + NONE, + kNonStrictMode); + + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii( + CStrVector("minimumFractionDigits")), + isolate->factory()->NewNumberFromInt( + number_format->getMinimumFractionDigits()), + NONE, + kNonStrictMode); + + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii( + CStrVector("maximumFractionDigits")), + isolate->factory()->NewNumberFromInt( + number_format->getMaximumFractionDigits()), + NONE, + kNonStrictMode); + + Handle key = isolate->factory()->NewStringFromAscii( + CStrVector("minimumSignificantDigits")); + if (resolved->HasLocalProperty(*key)) { + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii( + CStrVector("minimumSignificantDigits")), + isolate->factory()->NewNumberFromInt( + number_format->getMinimumSignificantDigits()), + NONE, + kNonStrictMode); + } + + key = isolate->factory()->NewStringFromAscii( + CStrVector("maximumSignificantDigits")); + if (resolved->HasLocalProperty(*key)) { + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii( + CStrVector("maximumSignificantDigits")), + isolate->factory()->NewNumberFromInt( + number_format->getMaximumSignificantDigits()), + NONE, + kNonStrictMode); + } + + // Set the locale + char result[ULOC_FULLNAME_CAPACITY]; + status = U_ZERO_ERROR; + uloc_toLanguageTag( + icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status); + if (U_SUCCESS(status)) { + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("locale")), + isolate->factory()->NewStringFromAscii(CStrVector(result)), + NONE, + kNonStrictMode); + } else { + // This would never happen, since we got the locale from ICU. + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("locale")), + isolate->factory()->NewStringFromAscii(CStrVector("und")), + NONE, + kNonStrictMode); + } +} + + +icu::Collator* CreateICUCollator( + Isolate* isolate, + const icu::Locale& icu_locale, + Handle options) { + // Make collator from options. + icu::Collator* collator = NULL; + UErrorCode status = U_ZERO_ERROR; + collator = icu::Collator::createInstance(icu_locale, status); + + if (U_FAILURE(status)) { + delete collator; + return NULL; + } + + // Set flags first, and then override them with sensitivity if necessary. + bool numeric; + if (ExtractBooleanSetting(isolate, options, "numeric", &numeric)) { + collator->setAttribute( + UCOL_NUMERIC_COLLATION, numeric ? UCOL_ON : UCOL_OFF, status); + } + + // Normalization is always on, by the spec. We are free to optimize + // if the strings are already normalized (but we don't have a way to tell + // that right now). + collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status); + + icu::UnicodeString case_first; + if (ExtractStringSetting(isolate, options, "caseFirst", &case_first)) { + if (case_first == UNICODE_STRING_SIMPLE("upper")) { + collator->setAttribute(UCOL_CASE_FIRST, UCOL_UPPER_FIRST, status); + } else if (case_first == UNICODE_STRING_SIMPLE("lower")) { + collator->setAttribute(UCOL_CASE_FIRST, UCOL_LOWER_FIRST, status); + } else { + // Default (false/off). + collator->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status); + } + } + + icu::UnicodeString sensitivity; + if (ExtractStringSetting(isolate, options, "sensitivity", &sensitivity)) { + if (sensitivity == UNICODE_STRING_SIMPLE("base")) { + collator->setStrength(icu::Collator::PRIMARY); + } else if (sensitivity == UNICODE_STRING_SIMPLE("accent")) { + collator->setStrength(icu::Collator::SECONDARY); + } else if (sensitivity == UNICODE_STRING_SIMPLE("case")) { + collator->setStrength(icu::Collator::PRIMARY); + collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status); + } else { + // variant (default) + collator->setStrength(icu::Collator::TERTIARY); + } + } + + bool ignore; + if (ExtractBooleanSetting(isolate, options, "ignorePunctuation", &ignore)) { + if (ignore) { + collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status); + } + } + + return collator; +} + + +void SetResolvedCollatorSettings(Isolate* isolate, + const icu::Locale& icu_locale, + icu::Collator* collator, + Handle resolved) { + UErrorCode status = U_ZERO_ERROR; + + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("numeric")), + isolate->factory()->ToBoolean( + collator->getAttribute(UCOL_NUMERIC_COLLATION, status) == UCOL_ON), + NONE, + kNonStrictMode); + + switch (collator->getAttribute(UCOL_CASE_FIRST, status)) { + case UCOL_LOWER_FIRST: + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("caseFirst")), + isolate->factory()->NewStringFromAscii(CStrVector("lower")), + NONE, + kNonStrictMode); + break; + case UCOL_UPPER_FIRST: + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("caseFirst")), + isolate->factory()->NewStringFromAscii(CStrVector("upper")), + NONE, + kNonStrictMode); + break; + default: + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("caseFirst")), + isolate->factory()->NewStringFromAscii(CStrVector("false")), + NONE, + kNonStrictMode); + } + + switch (collator->getAttribute(UCOL_STRENGTH, status)) { + case UCOL_PRIMARY: { + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("strength")), + isolate->factory()->NewStringFromAscii(CStrVector("primary")), + NONE, + kNonStrictMode); + + // case level: true + s1 -> case, s1 -> base. + if (UCOL_ON == collator->getAttribute(UCOL_CASE_LEVEL, status)) { + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")), + isolate->factory()->NewStringFromAscii(CStrVector("case")), + NONE, + kNonStrictMode); + } else { + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")), + isolate->factory()->NewStringFromAscii(CStrVector("base")), + NONE, + kNonStrictMode); + } + break; + } + case UCOL_SECONDARY: + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("strength")), + isolate->factory()->NewStringFromAscii(CStrVector("secondary")), + NONE, + kNonStrictMode); + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")), + isolate->factory()->NewStringFromAscii(CStrVector("accent")), + NONE, + kNonStrictMode); + break; + case UCOL_TERTIARY: + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("strength")), + isolate->factory()->NewStringFromAscii(CStrVector("tertiary")), + NONE, + kNonStrictMode); + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")), + isolate->factory()->NewStringFromAscii(CStrVector("variant")), + NONE, + kNonStrictMode); + break; + case UCOL_QUATERNARY: + // We shouldn't get quaternary and identical from ICU, but if we do + // put them into variant. + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("strength")), + isolate->factory()->NewStringFromAscii(CStrVector("quaternary")), + NONE, + kNonStrictMode); + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")), + isolate->factory()->NewStringFromAscii(CStrVector("variant")), + NONE, + kNonStrictMode); + break; + default: + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("strength")), + isolate->factory()->NewStringFromAscii(CStrVector("identical")), + NONE, + kNonStrictMode); + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")), + isolate->factory()->NewStringFromAscii(CStrVector("variant")), + NONE, + kNonStrictMode); + } + + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("ignorePunctuation")), + isolate->factory()->ToBoolean(collator->getAttribute( + UCOL_ALTERNATE_HANDLING, status) == UCOL_SHIFTED), + NONE, + kNonStrictMode); + + // Set the locale + char result[ULOC_FULLNAME_CAPACITY]; + status = U_ZERO_ERROR; + uloc_toLanguageTag( + icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status); + if (U_SUCCESS(status)) { + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("locale")), + isolate->factory()->NewStringFromAscii(CStrVector(result)), + NONE, + kNonStrictMode); + } else { + // This would never happen, since we got the locale from ICU. + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("locale")), + isolate->factory()->NewStringFromAscii(CStrVector("und")), + NONE, + kNonStrictMode); + } +} + } // namespace @@ -261,9 +775,10 @@ icu::SimpleDateFormat* DateFormat::InitializeDateTimeFormat( date_format = CreateICUDateFormat(isolate, no_extension_locale, options); // Set resolved settings (pattern, numbering system, calendar). - SetResolvedSettings(isolate, no_extension_locale, date_format, resolved); + SetResolvedDateSettings( + isolate, no_extension_locale, date_format, resolved); } else { - SetResolvedSettings(isolate, icu_locale, date_format, resolved); + SetResolvedDateSettings(isolate, icu_locale, date_format, resolved); } return date_format; @@ -273,8 +788,9 @@ icu::SimpleDateFormat* DateFormat::InitializeDateTimeFormat( icu::SimpleDateFormat* DateFormat::UnpackDateFormat( Isolate* isolate, Handle obj) { - if (obj->HasLocalProperty( - *isolate->factory()->NewStringFromAscii(CStrVector("dateFormat")))) { + Handle key = + isolate->factory()->NewStringFromAscii(CStrVector("dateFormat")); + if (obj->HasLocalProperty(*key)) { return reinterpret_cast( obj->GetInternalField(0)); } @@ -294,4 +810,129 @@ void DateFormat::DeleteDateFormat(v8::Isolate* isolate, object->Dispose(isolate); } + +icu::DecimalFormat* NumberFormat::InitializeNumberFormat( + Isolate* isolate, + Handle locale, + Handle options, + Handle resolved) { + // Convert BCP47 into ICU locale format. + UErrorCode status = U_ZERO_ERROR; + icu::Locale icu_locale; + char icu_result[ULOC_FULLNAME_CAPACITY]; + int icu_length = 0; + v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale)); + if (bcp47_locale.length() != 0) { + uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, + &icu_length, &status); + if (U_FAILURE(status) || icu_length == 0) { + return NULL; + } + icu_locale = icu::Locale(icu_result); + } + + icu::DecimalFormat* number_format = + CreateICUNumberFormat(isolate, icu_locale, options); + if (!number_format) { + // Remove extensions and try again. + icu::Locale no_extension_locale(icu_locale.getBaseName()); + number_format = CreateICUNumberFormat( + isolate, no_extension_locale, options); + + // Set resolved settings (pattern, numbering system). + SetResolvedNumberSettings( + isolate, no_extension_locale, number_format, resolved); + } else { + SetResolvedNumberSettings(isolate, icu_locale, number_format, resolved); + } + + return number_format; +} + + +icu::DecimalFormat* NumberFormat::UnpackNumberFormat( + Isolate* isolate, + Handle obj) { + Handle key = + isolate->factory()->NewStringFromAscii(CStrVector("numberFormat")); + if (obj->HasLocalProperty(*key)) { + return reinterpret_cast(obj->GetInternalField(0)); + } + + return NULL; +} + + +void NumberFormat::DeleteNumberFormat(v8::Isolate* isolate, + Persistent* object, + void* param) { + // First delete the hidden C++ object. + delete reinterpret_cast(Handle::cast( + v8::Utils::OpenPersistent(object))->GetInternalField(0)); + + // Then dispose of the persistent handle to JS object. + object->Dispose(isolate); +} + + +icu::Collator* Collator::InitializeCollator( + Isolate* isolate, + Handle locale, + Handle options, + Handle resolved) { + // Convert BCP47 into ICU locale format. + UErrorCode status = U_ZERO_ERROR; + icu::Locale icu_locale; + char icu_result[ULOC_FULLNAME_CAPACITY]; + int icu_length = 0; + v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale)); + if (bcp47_locale.length() != 0) { + uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY, + &icu_length, &status); + if (U_FAILURE(status) || icu_length == 0) { + return NULL; + } + icu_locale = icu::Locale(icu_result); + } + + icu::Collator* collator = CreateICUCollator(isolate, icu_locale, options); + if (!collator) { + // Remove extensions and try again. + icu::Locale no_extension_locale(icu_locale.getBaseName()); + collator = CreateICUCollator(isolate, no_extension_locale, options); + + // Set resolved settings (pattern, numbering system). + SetResolvedCollatorSettings( + isolate, no_extension_locale, collator, resolved); + } else { + SetResolvedCollatorSettings(isolate, icu_locale, collator, resolved); + } + + return collator; +} + + +icu::Collator* Collator::UnpackCollator(Isolate* isolate, + Handle obj) { + Handle key = + isolate->factory()->NewStringFromAscii(CStrVector("collator")); + if (obj->HasLocalProperty(*key)) { + return reinterpret_cast(obj->GetInternalField(0)); + } + + return NULL; +} + + +void Collator::DeleteCollator(v8::Isolate* isolate, + Persistent* object, + void* param) { + // First delete the hidden C++ object. + delete reinterpret_cast(Handle::cast( + v8::Utils::OpenPersistent(object))->GetInternalField(0)); + + // Then dispose of the persistent handle to JS object. + object->Dispose(isolate); +} + } } // namespace v8::internal diff --git a/deps/v8/src/i18n.h b/deps/v8/src/i18n.h index 37c57b1..5825ab6 100644 --- a/deps/v8/src/i18n.h +++ b/deps/v8/src/i18n.h @@ -33,6 +33,8 @@ #include "v8.h" namespace U_ICU_NAMESPACE { +class Collator; +class DecimalFormat; class SimpleDateFormat; } @@ -51,6 +53,7 @@ class I18N { I18N(); }; + class DateFormat { public: // Create a formatter for the specificied locale and options. Returns the @@ -74,6 +77,53 @@ class DateFormat { DateFormat(); }; + +class NumberFormat { + public: + // Create a formatter for the specificied locale and options. Returns the + // resolved settings for the locale / options. + static icu::DecimalFormat* InitializeNumberFormat( + Isolate* isolate, + Handle locale, + Handle options, + Handle resolved); + + // Unpacks number format object from corresponding JavaScript object. + static icu::DecimalFormat* UnpackNumberFormat(Isolate* isolate, + Handle obj); + + // Release memory we allocated for the NumberFormat once the JS object that + // holds the pointer gets garbage collected. + static void DeleteNumberFormat(v8::Isolate* isolate, + Persistent* object, + void* param); + private: + NumberFormat(); +}; + + +class Collator { + public: + // Create a collator for the specificied locale and options. Returns the + // resolved settings for the locale / options. + static icu::Collator* InitializeCollator( + Isolate* isolate, + Handle locale, + Handle options, + Handle resolved); + + // Unpacks collator object from corresponding JavaScript object. + static icu::Collator* UnpackCollator(Isolate* isolate, Handle obj); + + // Release memory we allocated for the Collator once the JS object that holds + // the pointer gets garbage collected. + static void DeleteCollator(v8::Isolate* isolate, + Persistent* object, + void* param); + private: + Collator(); +}; + } } // namespace v8::internal #endif // V8_I18N_H_ diff --git a/deps/v8/src/ia32/code-stubs-ia32.cc b/deps/v8/src/ia32/code-stubs-ia32.cc index 8721656..12cc499 100644 --- a/deps/v8/src/ia32/code-stubs-ia32.cc +++ b/deps/v8/src/ia32/code-stubs-ia32.cc @@ -3956,11 +3956,7 @@ void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm, Register scratch = scratch2; // Load the number string cache. - ExternalReference roots_array_start = - ExternalReference::roots_array_start(masm->isolate()); - __ mov(scratch, Immediate(Heap::kNumberStringCacheRootIndex)); - __ mov(number_string_cache, - Operand::StaticArray(scratch, times_pointer_size, roots_array_start)); + __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex); // Make the hash mask from the length of the number string cache. It // contains two elements (number and string) for each cache entry. __ mov(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset)); @@ -5010,9 +5006,6 @@ void InstanceofStub::Generate(MacroAssembler* masm) { static const int8_t kCmpEdiOperandByte2 = BitCast(0x3d); static const int8_t kMovEaxImmediateByte = BitCast(0xb8); - ExternalReference roots_array_start = - ExternalReference::roots_array_start(masm->isolate()); - ASSERT_EQ(object.code(), InstanceofStub::left().code()); ASSERT_EQ(function.code(), InstanceofStub::right().code()); @@ -5032,18 +5025,11 @@ void InstanceofStub::Generate(MacroAssembler* masm) { if (!HasCallSiteInlineCheck()) { // Look up the function and the map in the instanceof cache. Label miss; - __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex)); - __ cmp(function, Operand::StaticArray(scratch, - times_pointer_size, - roots_array_start)); + __ CompareRoot(function, scratch, Heap::kInstanceofCacheFunctionRootIndex); __ j(not_equal, &miss, Label::kNear); - __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex)); - __ cmp(map, Operand::StaticArray( - scratch, times_pointer_size, roots_array_start)); + __ CompareRoot(map, scratch, Heap::kInstanceofCacheMapRootIndex); __ j(not_equal, &miss, Label::kNear); - __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex)); - __ mov(eax, Operand::StaticArray( - scratch, times_pointer_size, roots_array_start)); + __ LoadRoot(eax, Heap::kInstanceofCacheAnswerRootIndex); __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize); __ bind(&miss); } @@ -5058,12 +5044,8 @@ void InstanceofStub::Generate(MacroAssembler* masm) { // Update the global instanceof or call site inlined cache with the current // map and function. The cached answer will be set when it is known below. if (!HasCallSiteInlineCheck()) { - __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex)); - __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start), - map); - __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex)); - __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start), - function); + __ StoreRoot(map, scratch, Heap::kInstanceofCacheMapRootIndex); + __ StoreRoot(function, scratch, Heap::kInstanceofCacheFunctionRootIndex); } else { // The constants for the code patching are based on no push instructions // at the call site. @@ -5097,10 +5079,8 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ bind(&is_instance); if (!HasCallSiteInlineCheck()) { - __ Set(eax, Immediate(0)); - __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex)); - __ mov(Operand::StaticArray(scratch, - times_pointer_size, roots_array_start), eax); + __ mov(eax, Immediate(0)); + __ StoreRoot(eax, scratch, Heap::kInstanceofCacheAnswerRootIndex); } else { // Get return address and delta to inlined map check. __ mov(eax, factory->true_value()); @@ -5119,10 +5099,8 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ bind(&is_not_instance); if (!HasCallSiteInlineCheck()) { - __ Set(eax, Immediate(Smi::FromInt(1))); - __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex)); - __ mov(Operand::StaticArray( - scratch, times_pointer_size, roots_array_start), eax); + __ mov(eax, Immediate(Smi::FromInt(1))); + __ StoreRoot(eax, scratch, Heap::kInstanceofCacheAnswerRootIndex); } else { // Get return address and delta to inlined map check. __ mov(eax, factory->false_value()); @@ -5875,11 +5853,7 @@ void StringHelper::GenerateTwoCharacterStringTableProbe(MacroAssembler* masm, // Load the string table. Register string_table = c2; - ExternalReference roots_array_start = - ExternalReference::roots_array_start(masm->isolate()); - __ mov(scratch, Immediate(Heap::kStringTableRootIndex)); - __ mov(string_table, - Operand::StaticArray(scratch, times_pointer_size, roots_array_start)); + __ LoadRoot(string_table, Heap::kStringTableRootIndex); // Calculate capacity mask from the string table capacity. Register mask = scratch2; @@ -5967,12 +5941,7 @@ void StringHelper::GenerateHashInit(MacroAssembler* masm, Register scratch) { // hash = (seed + character) + ((seed + character) << 10); if (Serializer::enabled()) { - ExternalReference roots_array_start = - ExternalReference::roots_array_start(masm->isolate()); - __ mov(scratch, Immediate(Heap::kHashSeedRootIndex)); - __ mov(scratch, Operand::StaticArray(scratch, - times_pointer_size, - roots_array_start)); + __ LoadRoot(scratch, Heap::kHashSeedRootIndex); __ SmiUntag(scratch); __ add(scratch, character); __ mov(hash, scratch); diff --git a/deps/v8/src/ia32/lithium-codegen-ia32.cc b/deps/v8/src/ia32/lithium-codegen-ia32.cc index 061ec9b..19c553b 100644 --- a/deps/v8/src/ia32/lithium-codegen-ia32.cc +++ b/deps/v8/src/ia32/lithium-codegen-ia32.cc @@ -355,6 +355,8 @@ bool LCodeGen::GenerateBody() { if (!CpuFeatures::IsSupported(SSE2)) FlushX87StackIfNecessary(instr); + RecordAndUpdatePosition(instr->position()); + instr->CompileToNative(this); if (!CpuFeatures::IsSupported(SSE2)) { @@ -422,6 +424,10 @@ bool LCodeGen::GenerateDeferredCode() { if (deferred_.length() > 0) { for (int i = 0; !is_aborted() && i < deferred_.length(); i++) { LDeferredCode* code = deferred_[i]; + + int pos = instructions_->at(code->instruction_index())->position(); + RecordAndUpdatePosition(pos); + Comment(";;; <@%d,#%d> " "-------------------- Deferred %s --------------------", code->instruction_index(), @@ -763,37 +769,57 @@ void LCodeGen::WriteTranslation(LEnvironment* environment, UNREACHABLE(); } + int object_index = 0; + int dematerialized_index = 0; for (int i = 0; i < translation_size; ++i) { LOperand* value = environment->values()->at(i); - - // TODO(mstarzinger): Introduce marker operands to indicate that this value - // is not present and must be reconstructed from the deoptimizer. Currently - // this is only used for the arguments object. - if (value == NULL) { - int arguments_count = environment->values()->length() - translation_size; - translation->BeginArgumentsObject(arguments_count); - for (int i = 0; i < arguments_count; ++i) { - LOperand* value = environment->values()->at(translation_size + i); - AddToTranslation(translation, - value, - environment->HasTaggedValueAt(translation_size + i), - environment->HasUint32ValueAt(translation_size + i)); - } - continue; - } - - AddToTranslation(translation, + AddToTranslation(environment, + translation, value, environment->HasTaggedValueAt(i), - environment->HasUint32ValueAt(i)); + environment->HasUint32ValueAt(i), + &object_index, + &dematerialized_index); } } -void LCodeGen::AddToTranslation(Translation* translation, +void LCodeGen::AddToTranslation(LEnvironment* environment, + Translation* translation, LOperand* op, bool is_tagged, - bool is_uint32) { + bool is_uint32, + int* object_index_pointer, + int* dematerialized_index_pointer) { + if (op == LEnvironment::materialization_marker()) { + int object_index = (*object_index_pointer)++; + if (environment->ObjectIsDuplicateAt(object_index)) { + int dupe_of = environment->ObjectDuplicateOfAt(object_index); + translation->DuplicateObject(dupe_of); + return; + } + int object_length = environment->ObjectLengthAt(object_index); + if (environment->ObjectIsArgumentsAt(object_index)) { + translation->BeginArgumentsObject(object_length); + } else { + translation->BeginCapturedObject(object_length); + } + int dematerialized_index = *dematerialized_index_pointer; + int env_offset = environment->translation_size() + dematerialized_index; + *dematerialized_index_pointer += object_length; + for (int i = 0; i < object_length; ++i) { + LOperand* value = environment->values()->at(env_offset + i); + AddToTranslation(environment, + translation, + value, + environment->HasTaggedValueAt(env_offset + i), + environment->HasUint32ValueAt(env_offset + i), + object_index_pointer, + dematerialized_index_pointer); + } + return; + } + if (op->IsStackSlot()) { if (is_tagged) { translation->StoreStackSlot(op->index()); @@ -984,7 +1010,7 @@ void LCodeGen::DeoptimizeIf(Condition cc, __ bind(&done); } - if (FLAG_trap_on_deopt && info()->IsOptimizing()) { + if (info()->ShouldTrapOnDeopt()) { Label done; if (cc != no_condition) __ j(NegateCondition(cc), &done, Label::kNear); __ int3(); @@ -1168,6 +1194,14 @@ void LCodeGen::RecordPosition(int position) { } +void LCodeGen::RecordAndUpdatePosition(int position) { + if (position >= 0 && position != old_position_) { + masm()->positions_recorder()->RecordPosition(position); + old_position_ = position; + } +} + + static const char* LabelType(LLabel* label) { if (label->is_loop_header()) return " (loop header)"; if (label->is_osr_entry()) return " (OSR entry)"; @@ -2199,6 +2233,17 @@ void LCodeGen::EmitBranch(InstrType instr, Condition cc) { } +template +void LCodeGen::EmitFalseBranch(InstrType instr, Condition cc) { + int false_block = instr->FalseDestination(chunk_); + if (cc == no_condition) { + __ jmp(chunk_->GetAssemblyLabel(false_block)); + } else { + __ j(cc, chunk_->GetAssemblyLabel(false_block)); + } +} + + void LCodeGen::DoIsNumberAndBranch(LIsNumberAndBranch* instr) { Representation r = instr->hydrogen()->value()->representation(); if (r.IsSmiOrInteger32() || r.IsDouble()) { @@ -2449,6 +2494,51 @@ void LCodeGen::DoCmpObjectEqAndBranch(LCmpObjectEqAndBranch* instr) { } +void LCodeGen::DoCmpHoleAndBranch(LCmpHoleAndBranch* instr) { + if (instr->hydrogen()->representation().IsTagged()) { + Register input_reg = ToRegister(instr->object()); + __ cmp(input_reg, factory()->the_hole_value()); + EmitBranch(instr, equal); + return; + } + + bool use_sse2 = CpuFeatures::IsSupported(SSE2); + if (use_sse2) { + CpuFeatureScope scope(masm(), SSE2); + XMMRegister input_reg = ToDoubleRegister(instr->object()); + __ ucomisd(input_reg, input_reg); + EmitFalseBranch(instr, parity_odd); + } else { + // Put the value to the top of stack + X87Register src = ToX87Register(instr->object()); + X87LoadForUsage(src); + __ fld(0); + __ fld(0); + __ FCmp(); + Label ok; + __ j(parity_even, &ok); + __ fstp(0); + EmitFalseBranch(instr, no_condition); + __ bind(&ok); + } + + + __ sub(esp, Immediate(kDoubleSize)); + if (use_sse2) { + CpuFeatureScope scope(masm(), SSE2); + XMMRegister input_reg = ToDoubleRegister(instr->object()); + __ movdbl(MemOperand(esp, 0), input_reg); + } else { + __ fstp_d(MemOperand(esp, 0)); + } + + __ add(esp, Immediate(kDoubleSize)); + int offset = sizeof(kHoleNanUpper32); + __ cmp(MemOperand(esp, -offset), Immediate(kHoleNanUpper32)); + EmitBranch(instr, equal); +} + + Condition LCodeGen::EmitIsObject(Register input, Register temp1, Label* is_not_object, @@ -3088,47 +3178,6 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) { } -void LCodeGen::EmitLoadFieldOrConstant(Register result, - Register object, - Handle type, - Handle name, - LEnvironment* env) { - LookupResult lookup(isolate()); - type->LookupDescriptor(NULL, *name, &lookup); - ASSERT(lookup.IsFound() || lookup.IsCacheable()); - if (lookup.IsField()) { - int index = lookup.GetLocalFieldIndexFromMap(*type); - int offset = index * kPointerSize; - if (index < 0) { - // Negative property indices are in-object properties, indexed - // from the end of the fixed part of the object. - __ mov(result, FieldOperand(object, offset + type->instance_size())); - } else { - // Non-negative property indices are in the properties array. - __ mov(result, FieldOperand(object, JSObject::kPropertiesOffset)); - __ mov(result, FieldOperand(result, offset + FixedArray::kHeaderSize)); - } - } else if (lookup.IsConstant()) { - Handle constant(lookup.GetConstantFromMap(*type), isolate()); - __ LoadObject(result, constant); - } else { - // Negative lookup. - // Check prototypes. - Handle current(HeapObject::cast((*type)->prototype())); - Heap* heap = type->GetHeap(); - while (*current != heap->null_value()) { - __ LoadHeapObject(result, current); - __ cmp(FieldOperand(result, HeapObject::kMapOffset), - Handle(current->map())); - DeoptimizeIf(not_equal, env); - current = - Handle(HeapObject::cast(current->map()->prototype())); - } - __ mov(result, factory()->undefined_value()); - } -} - - void LCodeGen::EmitPushTaggedOperand(LOperand* operand) { ASSERT(!operand->IsDoubleRegister()); if (operand->IsConstantOperand()) { @@ -3147,68 +3196,6 @@ void LCodeGen::EmitPushTaggedOperand(LOperand* operand) { } -// Check for cases where EmitLoadFieldOrConstantFunction needs to walk the -// prototype chain, which causes unbounded code generation. -static bool CompactEmit(SmallMapList* list, - Handle name, - int i, - Isolate* isolate) { - Handle map = list->at(i); - LookupResult lookup(isolate); - map->LookupDescriptor(NULL, *name, &lookup); - return lookup.IsField() || lookup.IsConstant(); -} - - -void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) { - Register object = ToRegister(instr->object()); - Register result = ToRegister(instr->result()); - - int map_count = instr->hydrogen()->types()->length(); - bool need_generic = instr->hydrogen()->need_generic(); - - if (map_count == 0 && !need_generic) { - DeoptimizeIf(no_condition, instr->environment()); - return; - } - Handle name = instr->hydrogen()->name(); - Label done; - bool all_are_compact = true; - for (int i = 0; i < map_count; ++i) { - if (!CompactEmit(instr->hydrogen()->types(), name, i, isolate())) { - all_are_compact = false; - break; - } - } - for (int i = 0; i < map_count; ++i) { - bool last = (i == map_count - 1); - Handle map = instr->hydrogen()->types()->at(i); - Label check_passed; - __ CompareMap(object, map, &check_passed); - if (last && !need_generic) { - DeoptimizeIf(not_equal, instr->environment()); - __ bind(&check_passed); - EmitLoadFieldOrConstant(result, object, map, name, instr->environment()); - } else { - Label next; - bool compact = all_are_compact ? true : - CompactEmit(instr->hydrogen()->types(), name, i, isolate()); - __ j(not_equal, &next, compact ? Label::kNear : Label::kFar); - __ bind(&check_passed); - EmitLoadFieldOrConstant(result, object, map, name, instr->environment()); - __ jmp(&done, all_are_compact ? Label::kNear : Label::kFar); - __ bind(&next); - } - } - if (need_generic) { - __ mov(ecx, name); - Handle ic = isolate()->builtins()->LoadIC_Initialize(); - CallCode(ic, RelocInfo::CODE_TARGET, instr); - } - __ bind(&done); -} - - void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) { ASSERT(ToRegister(instr->context()).is(esi)); ASSERT(ToRegister(instr->object()).is(edx)); @@ -4161,6 +4148,9 @@ void LCodeGen::DoMathExp(LMathExp* instr) { void LCodeGen::DoMathTan(LMathTan* instr) { ASSERT(ToDoubleRegister(instr->result()).is(xmm1)); + // Set the context register to a GC-safe fake value. Clobbering it is + // OK because this instruction is marked as a call. + __ Set(esi, Immediate(0)); TranscendentalCacheStub stub(TranscendentalCache::TAN, TranscendentalCacheStub::UNTAGGED); CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); @@ -4169,6 +4159,9 @@ void LCodeGen::DoMathTan(LMathTan* instr) { void LCodeGen::DoMathCos(LMathCos* instr) { ASSERT(ToDoubleRegister(instr->result()).is(xmm1)); + // Set the context register to a GC-safe fake value. Clobbering it is + // OK because this instruction is marked as a call. + __ Set(esi, Immediate(0)); TranscendentalCacheStub stub(TranscendentalCache::COS, TranscendentalCacheStub::UNTAGGED); CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); @@ -4177,6 +4170,9 @@ void LCodeGen::DoMathCos(LMathCos* instr) { void LCodeGen::DoMathSin(LMathSin* instr) { ASSERT(ToDoubleRegister(instr->result()).is(xmm1)); + // Set the context register to a GC-safe fake value. Clobbering it is + // OK because this instruction is marked as a call. + __ Set(esi, Immediate(0)); TranscendentalCacheStub stub(TranscendentalCache::SIN, TranscendentalCacheStub::UNTAGGED); CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); @@ -5049,13 +5045,6 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { Register reg = ToRegister(instr->result()); - bool convert_hole = false; - HValue* change_input = instr->hydrogen()->value(); - if (change_input->IsLoadKeyed()) { - HLoadKeyed* load = HLoadKeyed::cast(change_input); - convert_hole = load->UsesMustHandleHole(); - } - bool use_sse2 = CpuFeatures::IsSupported(SSE2); if (!use_sse2) { // Put the value to the top of stack @@ -5063,54 +5052,6 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { X87LoadForUsage(src); } - Label no_special_nan_handling; - Label done; - if (convert_hole) { - if (use_sse2) { - CpuFeatureScope scope(masm(), SSE2); - XMMRegister input_reg = ToDoubleRegister(instr->value()); - __ ucomisd(input_reg, input_reg); - } else { - __ fld(0); - __ fld(0); - __ FCmp(); - } - - __ j(parity_odd, &no_special_nan_handling); - __ sub(esp, Immediate(kDoubleSize)); - if (use_sse2) { - CpuFeatureScope scope(masm(), SSE2); - XMMRegister input_reg = ToDoubleRegister(instr->value()); - __ movdbl(MemOperand(esp, 0), input_reg); - } else { - __ fld(0); - __ fstp_d(MemOperand(esp, 0)); - } - __ cmp(MemOperand(esp, sizeof(kHoleNanLower32)), - Immediate(kHoleNanUpper32)); - Label canonicalize; - __ j(not_equal, &canonicalize); - __ add(esp, Immediate(kDoubleSize)); - __ mov(reg, factory()->the_hole_value()); - if (!use_sse2) { - __ fstp(0); - } - __ jmp(&done); - __ bind(&canonicalize); - __ add(esp, Immediate(kDoubleSize)); - ExternalReference nan = - ExternalReference::address_of_canonical_non_hole_nan(); - if (use_sse2) { - CpuFeatureScope scope(masm(), SSE2); - XMMRegister input_reg = ToDoubleRegister(instr->value()); - __ movdbl(input_reg, Operand::StaticVariable(nan)); - } else { - __ fstp(0); - __ fld_d(Operand::StaticVariable(nan)); - } - } - - __ bind(&no_special_nan_handling); DeferredNumberTagD* deferred = new(zone()) DeferredNumberTagD(this, instr); if (FLAG_inline_new) { Register tmp = ToRegister(instr->temp()); @@ -5126,7 +5067,6 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { } else { __ fstp_d(FieldOperand(reg, HeapNumber::kValueOffset)); } - __ bind(&done); } @@ -5176,23 +5116,21 @@ void LCodeGen::DoSmiUntag(LSmiUntag* instr) { void LCodeGen::EmitNumberUntagDNoSSE2(Register input_reg, Register temp_reg, X87Register res_reg, - bool allow_undefined_as_nan, + bool can_convert_undefined_to_nan, bool deoptimize_on_minus_zero, LEnvironment* env, NumberUntagDMode mode) { Label load_smi, done; X87PrepareToWrite(res_reg); - STATIC_ASSERT(NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE > - NUMBER_CANDIDATE_IS_ANY_TAGGED); - if (mode >= NUMBER_CANDIDATE_IS_ANY_TAGGED) { + if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED) { // Smi check. __ JumpIfSmi(input_reg, &load_smi, Label::kNear); // Heap number map check. __ cmp(FieldOperand(input_reg, HeapObject::kMapOffset), factory()->heap_number_map()); - if (!allow_undefined_as_nan) { + if (!can_convert_undefined_to_nan) { DeoptimizeIf(not_equal, env); } else { Label heap_number, convert; @@ -5200,10 +5138,6 @@ void LCodeGen::EmitNumberUntagDNoSSE2(Register input_reg, // Convert undefined (or hole) to NaN. __ cmp(input_reg, factory()->undefined_value()); - if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE) { - __ j(equal, &convert, Label::kNear); - __ cmp(input_reg, factory()->the_hole_value()); - } DeoptimizeIf(not_equal, env); __ bind(&convert); @@ -5250,22 +5184,20 @@ void LCodeGen::EmitNumberUntagDNoSSE2(Register input_reg, void LCodeGen::EmitNumberUntagD(Register input_reg, Register temp_reg, XMMRegister result_reg, - bool allow_undefined_as_nan, + bool can_convert_undefined_to_nan, bool deoptimize_on_minus_zero, LEnvironment* env, NumberUntagDMode mode) { Label load_smi, done; - STATIC_ASSERT(NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE > - NUMBER_CANDIDATE_IS_ANY_TAGGED); - if (mode >= NUMBER_CANDIDATE_IS_ANY_TAGGED) { + if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED) { // Smi check. __ JumpIfSmi(input_reg, &load_smi, Label::kNear); // Heap number map check. __ cmp(FieldOperand(input_reg, HeapObject::kMapOffset), factory()->heap_number_map()); - if (!allow_undefined_as_nan) { + if (!can_convert_undefined_to_nan) { DeoptimizeIf(not_equal, env); } else { Label heap_number, convert; @@ -5273,10 +5205,6 @@ void LCodeGen::EmitNumberUntagD(Register input_reg, // Convert undefined (and hole) to NaN. __ cmp(input_reg, factory()->undefined_value()); - if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE) { - __ j(equal, &convert, Label::kNear); - __ cmp(input_reg, factory()->the_hole_value()); - } DeoptimizeIf(not_equal, env); __ bind(&convert); @@ -5601,16 +5529,9 @@ void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) { instr->hydrogen()->deoptimize_on_minus_zero(); Register temp_reg = deoptimize_on_minus_zero ? ToRegister(temp) : no_reg; - NumberUntagDMode mode = NUMBER_CANDIDATE_IS_ANY_TAGGED; HValue* value = instr->hydrogen()->value(); - if (value->representation().IsSmi()) { - mode = NUMBER_CANDIDATE_IS_SMI; - } else if (value->IsLoadKeyed()) { - HLoadKeyed* load = HLoadKeyed::cast(value); - if (load->UsesMustHandleHole()) { - mode = NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE; - } - } + NumberUntagDMode mode = value->representation().IsSmi() + ? NUMBER_CANDIDATE_IS_SMI : NUMBER_CANDIDATE_IS_ANY_TAGGED; if (CpuFeatures::IsSupported(SSE2)) { CpuFeatureScope scope(masm(), SSE2); @@ -5618,7 +5539,7 @@ void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) { EmitNumberUntagD(input_reg, temp_reg, result_reg, - instr->hydrogen()->allow_undefined_as_nan(), + instr->hydrogen()->can_convert_undefined_to_nan(), deoptimize_on_minus_zero, instr->environment(), mode); @@ -5626,7 +5547,7 @@ void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) { EmitNumberUntagDNoSSE2(input_reg, temp_reg, ToX87Register(instr->result()), - instr->hydrogen()->allow_undefined_as_nan(), + instr->hydrogen()->can_convert_undefined_to_nan(), deoptimize_on_minus_zero, instr->environment(), mode); @@ -6356,6 +6277,7 @@ void LCodeGen::DoDeoptimize(LDeoptimize* instr) { if (info()->IsStub() && type == Deoptimizer::EAGER) { type = Deoptimizer::LAZY; } + Comment(";;; deoptimize: %s", instr->hydrogen()->reason()); DeoptimizeIf(no_condition, instr->environment(), type); } diff --git a/deps/v8/src/ia32/lithium-codegen-ia32.h b/deps/v8/src/ia32/lithium-codegen-ia32.h index c9a7899..aa8f6c2 100644 --- a/deps/v8/src/ia32/lithium-codegen-ia32.h +++ b/deps/v8/src/ia32/lithium-codegen-ia32.h @@ -71,7 +71,8 @@ class LCodeGen BASE_EMBEDDED { x87_stack_depth_(0), safepoints_(info->zone()), resolver_(this), - expected_safepoint_kind_(Safepoint::kSimple) { + expected_safepoint_kind_(Safepoint::kSimple), + old_position_(RelocInfo::kNoPosition) { PopulateDeoptimizationLiteralsWithInlinedFunctions(); } @@ -281,10 +282,13 @@ class LCodeGen BASE_EMBEDDED { void DeoptimizeIf(Condition cc, LEnvironment* environment); void ApplyCheckIf(Condition cc, LBoundsCheck* check); - void AddToTranslation(Translation* translation, + void AddToTranslation(LEnvironment* environment, + Translation* translation, LOperand* op, bool is_tagged, - bool is_uint32); + bool is_uint32, + int* object_index_pointer, + int* dematerialized_index_pointer); void RegisterDependentCodeForEmbeddedMaps(Handle code); void PopulateDeoptimizationData(Handle code); int DefineDeoptimizationLiteral(Handle literal); @@ -319,10 +323,14 @@ class LCodeGen BASE_EMBEDDED { Safepoint::DeoptMode mode); void RecordPosition(int position); + void RecordAndUpdatePosition(int position); + static Condition TokenToCondition(Token::Value op, bool is_unsigned); void EmitGoto(int block); template void EmitBranch(InstrType instr, Condition cc); + template + void EmitFalseBranch(InstrType instr, Condition cc); void EmitNumberUntagD( Register input, Register temp, @@ -369,12 +377,6 @@ class LCodeGen BASE_EMBEDDED { // Caller should branch on equal condition. void EmitIsConstructCall(Register temp); - void EmitLoadFieldOrConstant(Register result, - Register object, - Handle type, - Handle name, - LEnvironment* env); - // Emits optimized code to deep-copy the contents of statically known // object graphs (e.g. object literal boilerplate). void EmitDeepCopy(Handle object, @@ -448,6 +450,8 @@ class LCodeGen BASE_EMBEDDED { Safepoint::Kind expected_safepoint_kind_; + int old_position_; + class PushSafepointRegistersScope BASE_EMBEDDED { public: explicit PushSafepointRegistersScope(LCodeGen* codegen) diff --git a/deps/v8/src/ia32/lithium-ia32.cc b/deps/v8/src/ia32/lithium-ia32.cc index 52f39d4..b315868 100644 --- a/deps/v8/src/ia32/lithium-ia32.cc +++ b/deps/v8/src/ia32/lithium-ia32.cc @@ -645,8 +645,10 @@ LInstruction* LChunkBuilder::DefineFixedDouble( LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { HEnvironment* hydrogen_env = current_block_->last_environment(); int argument_index_accumulator = 0; + ZoneList objects_to_materialize(0, zone()); instr->set_environment(CreateEnvironment(hydrogen_env, - &argument_index_accumulator)); + &argument_index_accumulator, + &objects_to_materialize)); return instr; } @@ -868,7 +870,7 @@ void LChunkBuilder::DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block) { HEnvironment* last_environment = pred->last_environment(); for (int i = 0; i < block->phis()->length(); ++i) { HPhi* phi = block->phis()->at(i); - if (phi->merged_index() < last_environment->length()) { + if (phi->HasMergedIndex()) { last_environment->SetValueAt(phi->merged_index(), phi); } } @@ -938,6 +940,7 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { } #endif + instr->set_position(position_); if (FLAG_stress_pointer_maps && !instr->HasPointerMap()) { instr = AssignPointerMap(instr); } @@ -953,11 +956,13 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { LEnvironment* LChunkBuilder::CreateEnvironment( HEnvironment* hydrogen_env, - int* argument_index_accumulator) { + int* argument_index_accumulator, + ZoneList* objects_to_materialize) { if (hydrogen_env == NULL) return NULL; - LEnvironment* outer = - CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); + LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(), + argument_index_accumulator, + objects_to_materialize); BailoutId ast_id = hydrogen_env->ast_id(); ASSERT(!ast_id.IsNone() || hydrogen_env->frame_type() != JS_FUNCTION); @@ -972,16 +977,16 @@ LEnvironment* LChunkBuilder::CreateEnvironment( outer, hydrogen_env->entry(), zone()); - bool needs_arguments_object_materialization = false; int argument_index = *argument_index_accumulator; + int object_index = objects_to_materialize->length(); for (int i = 0; i < hydrogen_env->length(); ++i) { if (hydrogen_env->is_special_index(i)) continue; + LOperand* op; HValue* value = hydrogen_env->values()->at(i); - LOperand* op = NULL; - if (value->IsArgumentsObject()) { - needs_arguments_object_materialization = true; - op = NULL; + if (value->IsArgumentsObject() || value->IsCapturedObject()) { + objects_to_materialize->Add(value, zone()); + op = LEnvironment::materialization_marker(); } else if (value->IsPushArgument()) { op = new(zone()) LArgument(argument_index++); } else { @@ -992,15 +997,33 @@ LEnvironment* LChunkBuilder::CreateEnvironment( value->CheckFlag(HInstruction::kUint32)); } - if (needs_arguments_object_materialization) { - HArgumentsObject* arguments = hydrogen_env->entry() == NULL - ? graph()->GetArgumentsObject() - : hydrogen_env->entry()->arguments_object(); - ASSERT(arguments->IsLinked()); - for (int i = 1; i < arguments->arguments_count(); ++i) { - HValue* value = arguments->arguments_values()->at(i); - ASSERT(!value->IsArgumentsObject() && !value->IsPushArgument()); - LOperand* op = UseAny(value); + for (int i = object_index; i < objects_to_materialize->length(); ++i) { + HValue* object_to_materialize = objects_to_materialize->at(i); + int previously_materialized_object = -1; + for (int prev = 0; prev < i; ++prev) { + if (objects_to_materialize->at(prev) == objects_to_materialize->at(i)) { + previously_materialized_object = prev; + break; + } + } + int length = object_to_materialize->OperandCount(); + bool is_arguments = object_to_materialize->IsArgumentsObject(); + if (previously_materialized_object >= 0) { + result->AddDuplicateObject(previously_materialized_object); + continue; + } else { + result->AddNewObject(is_arguments ? length - 1 : length, is_arguments); + } + for (int i = is_arguments ? 1 : 0; i < length; ++i) { + LOperand* op; + HValue* value = object_to_materialize->OperandAt(i); + if (value->IsArgumentsObject() || value->IsCapturedObject()) { + objects_to_materialize->Add(value, zone()); + op = LEnvironment::materialization_marker(); + } else { + ASSERT(!value->IsPushArgument()); + op = UseAny(value); + } result->AddValue(op, value->representation(), value->CheckFlag(HInstruction::kUint32)); @@ -1392,9 +1415,8 @@ LInstruction* LChunkBuilder::DoShl(HShl* instr) { LInstruction* LChunkBuilder::DoBitwise(HBitwise* instr) { if (instr->representation().IsSmiOrInteger32()) { - ASSERT(instr->left()->representation().IsSmiOrInteger32()); - ASSERT(instr->right()->representation().Equals( - instr->left()->representation())); + ASSERT(instr->left()->representation().Equals(instr->representation())); + ASSERT(instr->right()->representation().Equals(instr->representation())); LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand()); LOperand* right = UseOrConstantAtStart(instr->BetterRightOperand()); @@ -1502,8 +1524,8 @@ LInstruction* LChunkBuilder::DoMod(HMod* instr) { HValue* left = instr->left(); HValue* right = instr->right(); if (instr->representation().IsSmiOrInteger32()) { - ASSERT(left->representation().IsSmiOrInteger32()); - ASSERT(right->representation().Equals(left->representation())); + ASSERT(instr->left()->representation().Equals(instr->representation())); + ASSERT(instr->right()->representation().Equals(instr->representation())); if (instr->HasPowerOf2Divisor()) { ASSERT(!right->CanBeZero()); @@ -1579,9 +1601,8 @@ LInstruction* LChunkBuilder::DoMul(HMul* instr) { LInstruction* LChunkBuilder::DoSub(HSub* instr) { if (instr->representation().IsSmiOrInteger32()) { - ASSERT(instr->left()->representation().IsSmiOrInteger32()); - ASSERT(instr->right()->representation().Equals( - instr->left()->representation())); + ASSERT(instr->left()->representation().Equals(instr->representation())); + ASSERT(instr->right()->representation().Equals(instr->representation())); LOperand* left = UseRegisterAtStart(instr->left()); LOperand* right = UseOrConstantAtStart(instr->right()); LSubI* sub = new(zone()) LSubI(left, right); @@ -1601,9 +1622,8 @@ LInstruction* LChunkBuilder::DoSub(HSub* instr) { LInstruction* LChunkBuilder::DoAdd(HAdd* instr) { if (instr->representation().IsSmiOrInteger32()) { - ASSERT(instr->left()->representation().IsSmiOrInteger32()); - ASSERT(instr->right()->representation().Equals( - instr->left()->representation())); + ASSERT(instr->left()->representation().Equals(instr->representation())); + ASSERT(instr->right()->representation().Equals(instr->representation())); // Check to see if it would be advantageous to use an lea instruction rather // than an add. This is the case when no overflow check is needed and there // are multiple uses of the add's inputs, so using a 3-register add will @@ -1636,9 +1656,8 @@ LInstruction* LChunkBuilder::DoMathMinMax(HMathMinMax* instr) { LOperand* left = NULL; LOperand* right = NULL; if (instr->representation().IsSmiOrInteger32()) { - ASSERT(instr->left()->representation().IsSmiOrInteger32()); - ASSERT(instr->right()->representation().Equals( - instr->left()->representation())); + ASSERT(instr->left()->representation().Equals(instr->representation())); + ASSERT(instr->right()->representation().Equals(instr->representation())); left = UseRegisterAtStart(instr->BetterLeftOperand()); right = UseOrConstantAtStart(instr->BetterRightOperand()); } else { @@ -1693,9 +1712,8 @@ LInstruction* LChunkBuilder::DoCompareNumericAndBranch( HCompareNumericAndBranch* instr) { Representation r = instr->representation(); if (r.IsSmiOrInteger32()) { - ASSERT(instr->left()->representation().IsSmiOrInteger32()); - ASSERT(instr->left()->representation().Equals( - instr->right()->representation())); + ASSERT(instr->left()->representation().Equals(r)); + ASSERT(instr->right()->representation().Equals(r)); LOperand* left = UseRegisterOrConstantAtStart(instr->left()); LOperand* right = UseOrConstantAtStart(instr->right()); return new(zone()) LCompareNumericAndBranch(left, right); @@ -1725,6 +1743,13 @@ LInstruction* LChunkBuilder::DoCompareObjectEqAndBranch( } +LInstruction* LChunkBuilder::DoCompareHoleAndBranch( + HCompareHoleAndBranch* instr) { + LOperand* object = UseRegisterAtStart(instr->object()); + return new(zone()) LCmpHoleAndBranch(object); +} + + LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) { ASSERT(instr->value()->representation().IsSmiOrTagged()); LOperand* temp = TempRegister(); @@ -2182,25 +2207,6 @@ LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) { } -LInstruction* LChunkBuilder::DoLoadNamedFieldPolymorphic( - HLoadNamedFieldPolymorphic* instr) { - ASSERT(instr->representation().IsTagged()); - if (instr->need_generic()) { - LOperand* context = UseFixed(instr->context(), esi); - LOperand* obj = UseFixed(instr->object(), edx); - LLoadNamedFieldPolymorphic* result = - new(zone()) LLoadNamedFieldPolymorphic(context, obj); - return MarkAsCall(DefineFixed(result, eax), instr); - } else { - LOperand* context = UseAny(instr->context()); // Not actually used. - LOperand* obj = UseRegisterAtStart(instr->object()); - LLoadNamedFieldPolymorphic* result = - new(zone()) LLoadNamedFieldPolymorphic(context, obj); - return AssignEnvironment(DefineAsRegister(result)); - } -} - - LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) { LOperand* context = UseFixed(instr->context(), esi); LOperand* object = UseFixed(instr->object(), edx); @@ -2567,6 +2573,12 @@ LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) { } +LInstruction* LChunkBuilder::DoCapturedObject(HCapturedObject* instr) { + // There are no real uses of a captured object. + return NULL; +} + + LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) { info()->MarkAsRequiresFrame(); LOperand* args = UseRegister(instr->arguments()); diff --git a/deps/v8/src/ia32/lithium-ia32.h b/deps/v8/src/ia32/lithium-ia32.h index effecb7..7ae87a0 100644 --- a/deps/v8/src/ia32/lithium-ia32.h +++ b/deps/v8/src/ia32/lithium-ia32.h @@ -75,6 +75,7 @@ class LCodeGen; V(ClassOfTestAndBranch) \ V(CompareNumericAndBranch) \ V(CmpObjectEqAndBranch) \ + V(CmpHoleAndBranch) \ V(CmpMapAndBranch) \ V(CmpT) \ V(ConstantD) \ @@ -127,7 +128,6 @@ class LCodeGen; V(LoadKeyed) \ V(LoadKeyedGeneric) \ V(LoadNamedField) \ - V(LoadNamedFieldPolymorphic) \ V(LoadNamedGeneric) \ V(MapEnumLength) \ V(MathAbs) \ @@ -209,7 +209,10 @@ class LInstruction: public ZoneObject { LInstruction() : environment_(NULL), hydrogen_value_(NULL), - is_call_(false) { } + bit_field_(IsCallBits::encode(false)) { + set_position(RelocInfo::kNoPosition); + } + virtual ~LInstruction() { } virtual void CompileToNative(LCodeGen* generator) = 0; @@ -248,19 +251,28 @@ class LInstruction: public ZoneObject { LPointerMap* pointer_map() const { return pointer_map_.get(); } bool HasPointerMap() const { return pointer_map_.is_set(); } + // The 31 bits PositionBits is used to store the int position value. And the + // position value may be RelocInfo::kNoPosition (-1). The accessor always + // +1/-1 so that the encoded value of position in bit_field_ is always >= 0 + // and can fit into the 31 bits PositionBits. + void set_position(int pos) { + bit_field_ = PositionBits::update(bit_field_, pos + 1); + } + int position() { return PositionBits::decode(bit_field_) - 1; } void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; } HValue* hydrogen_value() const { return hydrogen_value_; } virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { } - void MarkAsCall() { is_call_ = true; } + void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); } + bool IsCall() const { return IsCallBits::decode(bit_field_); } // Interface to the register allocator and iterators. - bool ClobbersTemps() const { return is_call_; } - bool ClobbersRegisters() const { return is_call_; } + bool ClobbersTemps() const { return IsCall(); } + bool ClobbersRegisters() const { return IsCall(); } virtual bool ClobbersDoubleRegisters() const { - return is_call_ || + return IsCall() || (!CpuFeatures::IsSupported(SSE2) && // We only have rudimentary X87Stack tracking, thus in general // cannot handle deoptimization nor phi-nodes. @@ -293,10 +305,13 @@ class LInstruction: public ZoneObject { virtual int TempCount() = 0; virtual LOperand* TempAt(int i) = 0; + class IsCallBits: public BitField {}; + class PositionBits: public BitField {}; + LEnvironment* environment_; SetOncePointer pointer_map_; HValue* hydrogen_value_; - bool is_call_; + int bit_field_; }; @@ -849,8 +864,20 @@ class LCmpObjectEqAndBranch: public LControlInstruction<2, 0> { LOperand* left() { return inputs_[0]; } LOperand* right() { return inputs_[1]; } - DECLARE_CONCRETE_INSTRUCTION(CmpObjectEqAndBranch, - "cmp-object-eq-and-branch") + DECLARE_CONCRETE_INSTRUCTION(CmpObjectEqAndBranch, "cmp-object-eq-and-branch") +}; + + +class LCmpHoleAndBranch: public LControlInstruction<1, 0> { + public: + explicit LCmpHoleAndBranch(LOperand* object) { + inputs_[0] = object; + } + + LOperand* object() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(CmpHoleAndBranch, "cmp-hole-and-branch") + DECLARE_HYDROGEN_ACCESSOR(CompareHoleAndBranch) }; @@ -1509,21 +1536,6 @@ class LLoadNamedField: public LTemplateInstruction<1, 1, 0> { }; -class LLoadNamedFieldPolymorphic: public LTemplateInstruction<1, 2, 0> { - public: - LLoadNamedFieldPolymorphic(LOperand* context, LOperand* object) { - inputs_[0] = context; - inputs_[1] = object; - } - - LOperand* context() { return inputs_[0]; } - LOperand* object() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field-polymorphic") - DECLARE_HYDROGEN_ACCESSOR(LoadNamedFieldPolymorphic) -}; - - class LLoadNamedGeneric: public LTemplateInstruction<1, 2, 0> { public: LLoadNamedGeneric(LOperand* context, LOperand* object) { @@ -2857,7 +2869,8 @@ class LChunkBuilder BASE_EMBEDDED { CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY); LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env, - int* argument_index_accumulator); + int* argument_index_accumulator, + ZoneList* objects_to_materialize); void VisitInstruction(HInstruction* current); diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc index 8b1be3c..67a7c0d 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/macro-assembler-ia32.cc @@ -54,6 +54,60 @@ MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size) } +void MacroAssembler::LoadRoot(Register destination, Heap::RootListIndex index) { + if (isolate()->heap()->RootCanBeTreatedAsConstant(index)) { + Handle value(&isolate()->heap()->roots_array_start()[index]); + mov(destination, value); + return; + } + ExternalReference roots_array_start = + ExternalReference::roots_array_start(isolate()); + mov(destination, Immediate(index)); + mov(destination, Operand::StaticArray(destination, + times_pointer_size, + roots_array_start)); +} + + +void MacroAssembler::StoreRoot(Register source, + Register scratch, + Heap::RootListIndex index) { + ASSERT(Heap::RootCanBeWrittenAfterInitialization(index)); + ExternalReference roots_array_start = + ExternalReference::roots_array_start(isolate()); + mov(scratch, Immediate(index)); + mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start), + source); +} + + +void MacroAssembler::CompareRoot(Register with, + Register scratch, + Heap::RootListIndex index) { + ExternalReference roots_array_start = + ExternalReference::roots_array_start(isolate()); + mov(scratch, Immediate(index)); + cmp(with, Operand::StaticArray(scratch, + times_pointer_size, + roots_array_start)); +} + + +void MacroAssembler::CompareRoot(Register with, Heap::RootListIndex index) { + ASSERT(isolate()->heap()->RootCanBeTreatedAsConstant(index)); + Handle value(&isolate()->heap()->roots_array_start()[index]); + cmp(with, value); +} + + +void MacroAssembler::CompareRoot(const Operand& with, + Heap::RootListIndex index) { + ASSERT(isolate()->heap()->RootCanBeTreatedAsConstant(index)); + Handle value(&isolate()->heap()->roots_array_start()[index]); + cmp(with, value); +} + + void MacroAssembler::InNewSpace( Register object, Register scratch, @@ -432,21 +486,6 @@ void MacroAssembler::SafePush(const Immediate& x) { } -void MacroAssembler::CompareRoot(Register with, Heap::RootListIndex index) { - // see ROOT_ACCESSOR macro in factory.h - Handle value(&isolate()->heap()->roots_array_start()[index]); - cmp(with, value); -} - - -void MacroAssembler::CompareRoot(const Operand& with, - Heap::RootListIndex index) { - // see ROOT_ACCESSOR macro in factory.h - Handle value(&isolate()->heap()->roots_array_start()[index]); - cmp(with, value); -} - - void MacroAssembler::CmpObjectType(Register heap_object, InstanceType type, Register map) { diff --git a/deps/v8/src/ia32/macro-assembler-ia32.h b/deps/v8/src/ia32/macro-assembler-ia32.h index 165c9ce..d537b0b 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.h +++ b/deps/v8/src/ia32/macro-assembler-ia32.h @@ -61,6 +61,15 @@ class MacroAssembler: public Assembler { // macro assembler. MacroAssembler(Isolate* isolate, void* buffer, int size); + // Operations on roots in the root-array. + void LoadRoot(Register destination, Heap::RootListIndex index); + void StoreRoot(Register source, Register scratch, Heap::RootListIndex index); + void CompareRoot(Register with, Register scratch, Heap::RootListIndex index); + // These methods can only be used with constant roots (i.e. non-writable + // and not in new space). + void CompareRoot(Register with, Heap::RootListIndex index); + void CompareRoot(const Operand& with, Heap::RootListIndex index); + // --------------------------------------------------------------------------- // GC Support enum RememberedSetFinalAction { @@ -362,10 +371,6 @@ class MacroAssembler: public Assembler { void SafeSet(Register dst, const Immediate& x); void SafePush(const Immediate& x); - // Compare against a known root, e.g. undefined, null, true, ... - void CompareRoot(Register with, Heap::RootListIndex index); - void CompareRoot(const Operand& with, Heap::RootListIndex index); - // Compare object type for heap object. // Incoming register is heap_object and outgoing register is map. void CmpObjectType(Register heap_object, InstanceType type, Register map); diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc index b7828b8..df7ad44 100644 --- a/deps/v8/src/ia32/stub-cache-ia32.cc +++ b/deps/v8/src/ia32/stub-cache-ia32.cc @@ -2479,6 +2479,8 @@ Handle CallStubCompiler::CompileMathAbsCall( STATIC_ASSERT(kSmiTag == 0); __ JumpIfNotSmi(eax, ¬_smi); + // Branchless abs implementation, refer to below: + // http://graphics.stanford.edu/~seander/bithacks.html#IntegerAbs // Set ebx to 1...1 (== -1) if the argument is negative, or to 0...0 // otherwise. __ mov(ebx, eax); diff --git a/deps/v8/src/isolate.cc b/deps/v8/src/isolate.cc index 448c719..7b77d89 100644 --- a/deps/v8/src/isolate.cc +++ b/deps/v8/src/isolate.cc @@ -1783,7 +1783,6 @@ Isolate::Isolate() regexp_stack_(NULL), date_cache_(NULL), code_stub_interface_descriptors_(NULL), - context_exit_happened_(false), initialized_from_snapshot_(false), cpu_profiler_(NULL), heap_profiler_(NULL), diff --git a/deps/v8/src/isolate.h b/deps/v8/src/isolate.h index 74bfc29..401505a 100644 --- a/deps/v8/src/isolate.h +++ b/deps/v8/src/isolate.h @@ -661,9 +661,9 @@ class Isolate { } inline Address* handler_address() { return &thread_local_top_.handler_; } - // Bottom JS entry (see StackTracer::Trace in sampler.cc). - static Address js_entry_sp(ThreadLocalTop* thread) { - return thread->js_entry_sp_; + // Bottom JS entry. + Address js_entry_sp() { + return thread_local_top_.js_entry_sp_; } inline Address* js_entry_sp_address() { return &thread_local_top_.js_entry_sp_; @@ -1062,13 +1062,6 @@ class Isolate { thread_local_top_.top_lookup_result_ = top; } - bool context_exit_happened() { - return context_exit_happened_; - } - void set_context_exit_happened(bool context_exit_happened) { - context_exit_happened_ = context_exit_happened; - } - bool initialized_from_snapshot() { return initialized_from_snapshot_; } double time_millis_since_init() { @@ -1317,10 +1310,6 @@ class Isolate { unibrow::Mapping interp_canonicalize_mapping_; CodeStubInterfaceDescriptor* code_stub_interface_descriptors_; - // The garbage collector should be a little more aggressive when it knows - // that a context was recently exited. - bool context_exit_happened_; - // True if this isolate was initialized from a snapshot. bool initialized_from_snapshot_; diff --git a/deps/v8/src/json-stringifier.h b/deps/v8/src/json-stringifier.h index 6e414cc..5ebdb40 100644 --- a/deps/v8/src/json-stringifier.h +++ b/deps/v8/src/json-stringifier.h @@ -601,6 +601,7 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSArraySlow( for (int i = 0; i < length; i++) { if (i > 0) Append(','); Handle element = Object::GetElement(object, i); + RETURN_IF_EMPTY_HANDLE_VALUE(isolate_, element, EXCEPTION); if (element->IsUndefined()) { AppendAscii("null"); } else { diff --git a/deps/v8/src/lithium.h b/deps/v8/src/lithium.h index 1e0784e..f773916 100644 --- a/deps/v8/src/lithium.h +++ b/deps/v8/src/lithium.h @@ -533,6 +533,7 @@ class LEnvironment: public ZoneObject { values_(value_count, zone), is_tagged_(value_count, zone), is_uint32_(value_count, zone), + object_mapping_(0, zone), outer_(outer), entry_(entry), zone_(zone) { } @@ -573,6 +574,38 @@ class LEnvironment: public ZoneObject { return is_uint32_.Contains(index); } + void AddNewObject(int length, bool is_arguments) { + uint32_t encoded = LengthOrDupeField::encode(length) | + IsArgumentsField::encode(is_arguments) | + IsDuplicateField::encode(false); + object_mapping_.Add(encoded, zone()); + } + + void AddDuplicateObject(int dupe_of) { + uint32_t encoded = LengthOrDupeField::encode(dupe_of) | + IsDuplicateField::encode(true); + object_mapping_.Add(encoded, zone()); + } + + int ObjectDuplicateOfAt(int index) { + ASSERT(ObjectIsDuplicateAt(index)); + return LengthOrDupeField::decode(object_mapping_[index]); + } + + int ObjectLengthAt(int index) { + ASSERT(!ObjectIsDuplicateAt(index)); + return LengthOrDupeField::decode(object_mapping_[index]); + } + + bool ObjectIsArgumentsAt(int index) { + ASSERT(!ObjectIsDuplicateAt(index)); + return IsArgumentsField::decode(object_mapping_[index]); + } + + bool ObjectIsDuplicateAt(int index) { + return IsDuplicateField::decode(object_mapping_[index]); + } + void Register(int deoptimization_index, int translation_index, int pc_offset) { @@ -587,6 +620,14 @@ class LEnvironment: public ZoneObject { void PrintTo(StringStream* stream); + // Marker value indicating a de-materialized object. + static LOperand* materialization_marker() { return NULL; } + + // Encoding used for the object_mapping map below. + class LengthOrDupeField : public BitField { }; + class IsArgumentsField : public BitField { }; + class IsDuplicateField : public BitField { }; + private: Handle closure_; FrameType frame_type_; @@ -603,6 +644,10 @@ class LEnvironment: public ZoneObject { ZoneList values_; GrowableBitVector is_tagged_; GrowableBitVector is_uint32_; + + // Map with encoded information about materialization_marker operands. + ZoneList object_mapping_; + LEnvironment* outer_; HEnterInlined* entry_; Zone* zone_; @@ -754,8 +799,7 @@ int StackSlotOffset(int index); enum NumberUntagDMode { NUMBER_CANDIDATE_IS_SMI, - NUMBER_CANDIDATE_IS_ANY_TAGGED, - NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE + NUMBER_CANDIDATE_IS_ANY_TAGGED }; diff --git a/deps/v8/src/liveedit.cc b/deps/v8/src/liveedit.cc index 859cf2b..406510a 100644 --- a/deps/v8/src/liveedit.cc +++ b/deps/v8/src/liveedit.cc @@ -1290,6 +1290,7 @@ MaybeObject* LiveEdit::ReplaceFunctionCode( if (code_scope_info->IsFixedArray()) { shared_info->set_scope_info(ScopeInfo::cast(*code_scope_info)); } + shared_info->DisableOptimization(kLiveEdit); } if (shared_info->debug_info()->IsDebugInfo()) { diff --git a/deps/v8/src/mips/codegen-mips.cc b/deps/v8/src/mips/codegen-mips.cc index 10490e7..5c847fc 100644 --- a/deps/v8/src/mips/codegen-mips.cc +++ b/deps/v8/src/mips/codegen-mips.cc @@ -205,7 +205,7 @@ void ElementsTransitionGenerator::GenerateSmiToDouble( // Allocate new FixedDoubleArray. __ sll(scratch, t1, 2); __ Addu(scratch, scratch, FixedDoubleArray::kHeaderSize); - __ Allocate(scratch, t2, t3, t5, &gc_required, NO_ALLOCATION_FLAGS); + __ Allocate(scratch, t2, t3, t5, &gc_required, DOUBLE_ALIGNMENT); // t2: destination FixedDoubleArray, not tagged as heap object // Set destination FixedDoubleArray's length and map. diff --git a/deps/v8/src/mips/lithium-codegen-mips.cc b/deps/v8/src/mips/lithium-codegen-mips.cc index 34e601c..2bc52e4 100644 --- a/deps/v8/src/mips/lithium-codegen-mips.cc +++ b/deps/v8/src/mips/lithium-codegen-mips.cc @@ -268,6 +268,8 @@ bool LCodeGen::GenerateBody() { instr->Mnemonic()); } + RecordAndUpdatePosition(instr->position()); + instr->CompileToNative(this); } EnsureSpaceForLazyDeopt(); @@ -281,6 +283,10 @@ bool LCodeGen::GenerateDeferredCode() { if (deferred_.length() > 0) { for (int i = 0; !is_aborted() && i < deferred_.length(); i++) { LDeferredCode* code = deferred_[i]; + + int pos = instructions_->at(code->instruction_index())->position(); + RecordAndUpdatePosition(pos); + Comment(";;; <@%d,#%d> " "-------------------- Deferred %s --------------------", code->instruction_index(), @@ -591,37 +597,57 @@ void LCodeGen::WriteTranslation(LEnvironment* environment, break; } + int object_index = 0; + int dematerialized_index = 0; for (int i = 0; i < translation_size; ++i) { LOperand* value = environment->values()->at(i); - - // TODO(mstarzinger): Introduce marker operands to indicate that this value - // is not present and must be reconstructed from the deoptimizer. Currently - // this is only used for the arguments object. - if (value == NULL) { - int arguments_count = environment->values()->length() - translation_size; - translation->BeginArgumentsObject(arguments_count); - for (int i = 0; i < arguments_count; ++i) { - LOperand* value = environment->values()->at(translation_size + i); - AddToTranslation(translation, - value, - environment->HasTaggedValueAt(translation_size + i), - environment->HasUint32ValueAt(translation_size + i)); - } - continue; - } - - AddToTranslation(translation, + AddToTranslation(environment, + translation, value, environment->HasTaggedValueAt(i), - environment->HasUint32ValueAt(i)); + environment->HasUint32ValueAt(i), + &object_index, + &dematerialized_index); } } -void LCodeGen::AddToTranslation(Translation* translation, +void LCodeGen::AddToTranslation(LEnvironment* environment, + Translation* translation, LOperand* op, bool is_tagged, - bool is_uint32) { + bool is_uint32, + int* object_index_pointer, + int* dematerialized_index_pointer) { + if (op == LEnvironment::materialization_marker()) { + int object_index = (*object_index_pointer)++; + if (environment->ObjectIsDuplicateAt(object_index)) { + int dupe_of = environment->ObjectDuplicateOfAt(object_index); + translation->DuplicateObject(dupe_of); + return; + } + int object_length = environment->ObjectLengthAt(object_index); + if (environment->ObjectIsArgumentsAt(object_index)) { + translation->BeginArgumentsObject(object_length); + } else { + translation->BeginCapturedObject(object_length); + } + int dematerialized_index = *dematerialized_index_pointer; + int env_offset = environment->translation_size() + dematerialized_index; + *dematerialized_index_pointer += object_length; + for (int i = 0; i < object_length; ++i) { + LOperand* value = environment->values()->at(env_offset + i); + AddToTranslation(environment, + translation, + value, + environment->HasTaggedValueAt(env_offset + i), + environment->HasUint32ValueAt(env_offset + i), + object_index_pointer, + dematerialized_index_pointer); + } + return; + } + if (op->IsStackSlot()) { if (is_tagged) { translation->StoreStackSlot(op->index()); @@ -761,7 +787,7 @@ void LCodeGen::DeoptimizeIf(Condition cc, return; } - if (FLAG_trap_on_deopt && info()->IsOptimizing()) { + if (info()->ShouldTrapOnDeopt()) { Label skip; if (cc != al) { __ Branch(&skip, NegateCondition(cc), src1, src2); @@ -960,6 +986,14 @@ void LCodeGen::RecordPosition(int position) { } +void LCodeGen::RecordAndUpdatePosition(int position) { + if (position >= 0 && position != old_position_) { + masm()->positions_recorder()->RecordPosition(position); + old_position_ = position; + } +} + + static const char* LabelType(LLabel* label) { if (label->is_loop_header()) return " (loop header)"; if (label->is_osr_entry()) return " (OSR entry)"; @@ -1154,7 +1188,6 @@ void LCodeGen::EmitSignedIntegerDivisionByConstant( Register scratch, LEnvironment* environment) { ASSERT(!AreAliased(dividend, scratch, at, no_reg)); - ASSERT(LChunkBuilder::HasMagicNumberForDivisor(divisor)); uint32_t divisor_abs = abs(divisor); @@ -2888,90 +2921,6 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) { } -void LCodeGen::EmitLoadFieldOrConstantFunction(Register result, - Register object, - Handle type, - Handle name, - LEnvironment* env) { - LookupResult lookup(isolate()); - type->LookupDescriptor(NULL, *name, &lookup); - ASSERT(lookup.IsFound() || lookup.IsCacheable()); - if (lookup.IsField()) { - int index = lookup.GetLocalFieldIndexFromMap(*type); - int offset = index * kPointerSize; - if (index < 0) { - // Negative property indices are in-object properties, indexed - // from the end of the fixed part of the object. - __ lw(result, FieldMemOperand(object, offset + type->instance_size())); - } else { - // Non-negative property indices are in the properties array. - __ lw(result, FieldMemOperand(object, JSObject::kPropertiesOffset)); - __ lw(result, FieldMemOperand(result, offset + FixedArray::kHeaderSize)); - } - } else if (lookup.IsConstant()) { - Handle constant(lookup.GetConstantFromMap(*type), isolate()); - __ LoadObject(result, constant); - } else { - // Negative lookup. - // Check prototypes. - Handle current(HeapObject::cast((*type)->prototype())); - Heap* heap = type->GetHeap(); - while (*current != heap->null_value()) { - __ LoadHeapObject(result, current); - __ lw(result, FieldMemOperand(result, HeapObject::kMapOffset)); - DeoptimizeIf(ne, env, result, Operand(Handle(current->map()))); - current = - Handle(HeapObject::cast(current->map()->prototype())); - } - __ LoadRoot(result, Heap::kUndefinedValueRootIndex); - } -} - - -void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) { - Register object = ToRegister(instr->object()); - Register result = ToRegister(instr->result()); - Register object_map = scratch0(); - - int map_count = instr->hydrogen()->types()->length(); - bool need_generic = instr->hydrogen()->need_generic(); - - if (map_count == 0 && !need_generic) { - DeoptimizeIf(al, instr->environment()); - return; - } - Handle name = instr->hydrogen()->name(); - Label done; - __ lw(object_map, FieldMemOperand(object, HeapObject::kMapOffset)); - for (int i = 0; i < map_count; ++i) { - bool last = (i == map_count - 1); - Handle map = instr->hydrogen()->types()->at(i); - Label check_passed; - __ CompareMapAndBranch(object_map, map, &check_passed, eq, &check_passed); - if (last && !need_generic) { - DeoptimizeIf(al, instr->environment()); - __ bind(&check_passed); - EmitLoadFieldOrConstantFunction( - result, object, map, name, instr->environment()); - } else { - Label next; - __ Branch(&next); - __ bind(&check_passed); - EmitLoadFieldOrConstantFunction( - result, object, map, name, instr->environment()); - __ Branch(&done); - __ bind(&next); - } - } - if (need_generic) { - __ li(a2, Operand(name)); - Handle ic = isolate()->builtins()->LoadIC_Initialize(); - CallCode(ic, RelocInfo::CODE_TARGET, instr); - } - __ bind(&done); -} - - void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) { ASSERT(ToRegister(instr->object()).is(a0)); ASSERT(ToRegister(instr->result()).is(v0)); @@ -4456,12 +4405,13 @@ void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg, scratch, GetRAState(), kDontSaveFPRegs); } else { - PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); + PushSafepointRegistersScope scope( + this, Safepoint::kWithRegistersAndDoubles); __ mov(a0, object_reg); __ li(a1, Operand(to_map)); TransitionElementsKindStub stub(from_kind, to_kind); __ CallStub(&stub); - RecordSafepointWithRegisters( + RecordSafepointWithRegistersAndDoubles( instr->pointer_map(), 0, Safepoint::kNoLazyDeopt); } __ bind(¬_applicable); @@ -4767,7 +4717,7 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { __ Move(reg, scratch0(), input_reg); Label canonicalize; __ Branch(&canonicalize, ne, scratch0(), Operand(kHoleNanUpper32)); - __ li(reg, factory()->the_hole_value()); + __ li(reg, factory()->undefined_value()); __ Branch(&done); __ bind(&canonicalize); __ Move(input_reg, @@ -5172,7 +5122,7 @@ void LCodeGen::DoCheckFunction(LCheckFunction* instr) { AllowDeferredHandleDereference smi_check; if (isolate()->heap()->InNewSpace(*target)) { Register reg = ToRegister(instr->value()); - Handle cell = isolate()->factory()->NewPropertyCell(target); + Handle cell = isolate()->factory()->NewCell(target); __ li(at, Operand(Handle(cell))); __ lw(at, FieldMemOperand(at, Cell::kValueOffset)); DeoptimizeIf(ne, instr->environment(), reg, @@ -5234,11 +5184,11 @@ void LCodeGen::DoCheckMaps(LCheckMaps* instr) { __ CompareMapAndBranch(map_reg, map, &success, eq, &success); } Handle map = map_set->last(); - __ CompareMapAndBranch(map_reg, map, &success, eq, &success); + // Do the CompareMap() directly within the Branch() and DeoptimizeIf(). if (instr->hydrogen()->has_migration_target()) { - __ Branch(deferred->entry()); + __ Branch(deferred->entry(), ne, map_reg, Operand(map)); } else { - DeoptimizeIf(al, instr->environment()); + DeoptimizeIf(ne, instr->environment(), map_reg, Operand(map)); } __ bind(&success); @@ -5675,6 +5625,8 @@ void LCodeGen::DoDeoptimize(LDeoptimize* instr) { if (info()->IsStub() && type == Deoptimizer::EAGER) { type = Deoptimizer::LAZY; } + + Comment(";;; deoptimize: %s", instr->hydrogen()->reason()); DeoptimizeIf(al, instr->environment(), type, zero_reg, Operand(zero_reg)); } diff --git a/deps/v8/src/mips/lithium-codegen-mips.h b/deps/v8/src/mips/lithium-codegen-mips.h index 670c4cc..b97a3cd 100644 --- a/deps/v8/src/mips/lithium-codegen-mips.h +++ b/deps/v8/src/mips/lithium-codegen-mips.h @@ -65,7 +65,8 @@ class LCodeGen BASE_EMBEDDED { frame_is_built_(false), safepoints_(info->zone()), resolver_(this), - expected_safepoint_kind_(Safepoint::kSimple) { + expected_safepoint_kind_(Safepoint::kSimple), + old_position_(RelocInfo::kNoPosition) { PopulateDeoptimizationLiteralsWithInlinedFunctions(); } @@ -290,10 +291,13 @@ class LCodeGen BASE_EMBEDDED { Register src1 = zero_reg, const Operand& src2 = Operand(zero_reg)); - void AddToTranslation(Translation* translation, + void AddToTranslation(LEnvironment* environment, + Translation* translation, LOperand* op, bool is_tagged, - bool is_uint32); + bool is_uint32, + int* object_index_pointer, + int* dematerialized_index_pointer); void RegisterDependentCodeForEmbeddedMaps(Handle code); void PopulateDeoptimizationData(Handle code); int DefineDeoptimizationLiteral(Handle literal); @@ -319,6 +323,7 @@ class LCodeGen BASE_EMBEDDED { int arguments, Safepoint::DeoptMode mode); void RecordPosition(int position); + void RecordAndUpdatePosition(int position); static Condition TokenToCondition(Token::Value op, bool is_unsigned); void EmitGoto(int block); @@ -373,12 +378,6 @@ class LCodeGen BASE_EMBEDDED { // Caller should branch on equal condition. void EmitIsConstructCall(Register temp1, Register temp2); - void EmitLoadFieldOrConstantFunction(Register result, - Register object, - Handle type, - Handle name, - LEnvironment* env); - // Emits optimized code to deep-copy the contents of statically known // object graphs (e.g. object literal boilerplate). void EmitDeepCopy(Handle object, @@ -435,6 +434,8 @@ class LCodeGen BASE_EMBEDDED { Safepoint::Kind expected_safepoint_kind_; + int old_position_; + class PushSafepointRegistersScope BASE_EMBEDDED { public: PushSafepointRegistersScope(LCodeGen* codegen, diff --git a/deps/v8/src/mips/lithium-mips.cc b/deps/v8/src/mips/lithium-mips.cc index 38ac19f..23f48a7 100644 --- a/deps/v8/src/mips/lithium-mips.cc +++ b/deps/v8/src/mips/lithium-mips.cc @@ -598,8 +598,10 @@ LInstruction* LChunkBuilder::DefineFixedDouble( LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { HEnvironment* hydrogen_env = current_block_->last_environment(); int argument_index_accumulator = 0; + ZoneList objects_to_materialize(0, zone()); instr->set_environment(CreateEnvironment(hydrogen_env, - &argument_index_accumulator)); + &argument_index_accumulator, + &objects_to_materialize)); return instr; } @@ -818,7 +820,7 @@ void LChunkBuilder::DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block) { HEnvironment* last_environment = pred->last_environment(); for (int i = 0; i < block->phis()->length(); ++i) { HPhi* phi = block->phis()->at(i); - if (phi->merged_index() < last_environment->length()) { + if (phi->HasMergedIndex()) { last_environment->SetValueAt(phi->merged_index(), phi); } } @@ -888,6 +890,7 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { } #endif + instr->set_position(position_); if (FLAG_stress_pointer_maps && !instr->HasPointerMap()) { instr = AssignPointerMap(instr); } @@ -903,11 +906,13 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { LEnvironment* LChunkBuilder::CreateEnvironment( HEnvironment* hydrogen_env, - int* argument_index_accumulator) { + int* argument_index_accumulator, + ZoneList* objects_to_materialize) { if (hydrogen_env == NULL) return NULL; - LEnvironment* outer = - CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); + LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(), + argument_index_accumulator, + objects_to_materialize); BailoutId ast_id = hydrogen_env->ast_id(); ASSERT(!ast_id.IsNone() || hydrogen_env->frame_type() != JS_FUNCTION); @@ -922,16 +927,16 @@ LEnvironment* LChunkBuilder::CreateEnvironment( outer, hydrogen_env->entry(), zone()); - bool needs_arguments_object_materialization = false; int argument_index = *argument_index_accumulator; + int object_index = objects_to_materialize->length(); for (int i = 0; i < hydrogen_env->length(); ++i) { if (hydrogen_env->is_special_index(i)) continue; + LOperand* op; HValue* value = hydrogen_env->values()->at(i); - LOperand* op = NULL; - if (value->IsArgumentsObject()) { - needs_arguments_object_materialization = true; - op = NULL; + if (value->IsArgumentsObject() || value->IsCapturedObject()) { + objects_to_materialize->Add(value, zone()); + op = LEnvironment::materialization_marker(); } else if (value->IsPushArgument()) { op = new(zone()) LArgument(argument_index++); } else { @@ -942,15 +947,33 @@ LEnvironment* LChunkBuilder::CreateEnvironment( value->CheckFlag(HInstruction::kUint32)); } - if (needs_arguments_object_materialization) { - HArgumentsObject* arguments = hydrogen_env->entry() == NULL - ? graph()->GetArgumentsObject() - : hydrogen_env->entry()->arguments_object(); - ASSERT(arguments->IsLinked()); - for (int i = 1; i < arguments->arguments_count(); ++i) { - HValue* value = arguments->arguments_values()->at(i); - ASSERT(!value->IsArgumentsObject() && !value->IsPushArgument()); - LOperand* op = UseAny(value); + for (int i = object_index; i < objects_to_materialize->length(); ++i) { + HValue* object_to_materialize = objects_to_materialize->at(i); + int previously_materialized_object = -1; + for (int prev = 0; prev < i; ++prev) { + if (objects_to_materialize->at(prev) == objects_to_materialize->at(i)) { + previously_materialized_object = prev; + break; + } + } + int length = object_to_materialize->OperandCount(); + bool is_arguments = object_to_materialize->IsArgumentsObject(); + if (previously_materialized_object >= 0) { + result->AddDuplicateObject(previously_materialized_object); + continue; + } else { + result->AddNewObject(is_arguments ? length - 1 : length, is_arguments); + } + for (int i = is_arguments ? 1 : 0; i < length; ++i) { + LOperand* op; + HValue* value = object_to_materialize->OperandAt(i); + if (value->IsArgumentsObject() || value->IsCapturedObject()) { + objects_to_materialize->Add(value, zone()); + op = LEnvironment::materialization_marker(); + } else { + ASSERT(!value->IsPushArgument()); + op = UseAny(value); + } result->AddValue(op, value->representation(), value->CheckFlag(HInstruction::kUint32)); @@ -1389,8 +1412,6 @@ LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) { LOperand* dividend = UseRegister(instr->left()); LOperand* divisor = UseRegisterOrConstant(right); LOperand* remainder = TempRegister(); - ASSERT(right->IsConstant() && - HConstant::cast(right)->HasInteger32Value()); return AssignEnvironment(DefineAsRegister( new(zone()) LMathFloorOfDiv(dividend, divisor, remainder))); } @@ -1607,9 +1628,8 @@ LInstruction* LChunkBuilder::DoCompareNumericAndBranch( HCompareNumericAndBranch* instr) { Representation r = instr->representation(); if (r.IsSmiOrInteger32()) { - ASSERT(instr->left()->representation().IsSmiOrInteger32()); - ASSERT(instr->left()->representation().Equals( - instr->right()->representation())); + ASSERT(instr->left()->representation().Equals(r)); + ASSERT(instr->right()->representation().Equals(r)); LOperand* left = UseRegisterOrConstantAtStart(instr->left()); LOperand* right = UseRegisterOrConstantAtStart(instr->right()); return new(zone()) LCompareNumericAndBranch(left, right); @@ -2046,23 +2066,6 @@ LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) { } -LInstruction* LChunkBuilder::DoLoadNamedFieldPolymorphic( - HLoadNamedFieldPolymorphic* instr) { - ASSERT(instr->representation().IsTagged()); - if (instr->need_generic()) { - LOperand* obj = UseFixed(instr->object(), a0); - LLoadNamedFieldPolymorphic* result = - new(zone()) LLoadNamedFieldPolymorphic(obj); - return MarkAsCall(DefineFixed(result, v0), instr); - } else { - LOperand* obj = UseRegisterAtStart(instr->object()); - LLoadNamedFieldPolymorphic* result = - new(zone()) LLoadNamedFieldPolymorphic(obj); - return AssignEnvironment(DefineAsRegister(result)); - } -} - - LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) { LOperand* object = UseFixed(instr->object(), a0); LInstruction* result = DefineFixed(new(zone()) LLoadNamedGeneric(object), v0); @@ -2360,6 +2363,12 @@ LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) { } +LInstruction* LChunkBuilder::DoCapturedObject(HCapturedObject* instr) { + // There are no real uses of a captured object. + return NULL; +} + + LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) { info()->MarkAsRequiresFrame(); LOperand* args = UseRegister(instr->arguments()); diff --git a/deps/v8/src/mips/lithium-mips.h b/deps/v8/src/mips/lithium-mips.h index a21c323..a1792b1 100644 --- a/deps/v8/src/mips/lithium-mips.h +++ b/deps/v8/src/mips/lithium-mips.h @@ -126,7 +126,6 @@ class LCodeGen; V(LoadKeyed) \ V(LoadKeyedGeneric) \ V(LoadNamedField) \ - V(LoadNamedFieldPolymorphic) \ V(LoadNamedGeneric) \ V(MapEnumLength) \ V(MathAbs) \ @@ -205,9 +204,12 @@ class LCodeGen; class LInstruction: public ZoneObject { public: LInstruction() - : environment_(NULL), - hydrogen_value_(NULL), - is_call_(false) { } + : environment_(NULL), + hydrogen_value_(NULL), + bit_field_(IsCallBits::encode(false)) { + set_position(RelocInfo::kNoPosition); + } + virtual ~LInstruction() { } virtual void CompileToNative(LCodeGen* generator) = 0; @@ -246,20 +248,30 @@ class LInstruction: public ZoneObject { LPointerMap* pointer_map() const { return pointer_map_.get(); } bool HasPointerMap() const { return pointer_map_.is_set(); } + // The 31 bits PositionBits is used to store the int position value. And the + // position value may be RelocInfo::kNoPosition (-1). The accessor always + // +1/-1 so that the encoded value of position in bit_field_ is always >= 0 + // and can fit into the 31 bits PositionBits. + void set_position(int pos) { + bit_field_ = PositionBits::update(bit_field_, pos + 1); + } + int position() { return PositionBits::decode(bit_field_) - 1; } + void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; } HValue* hydrogen_value() const { return hydrogen_value_; } virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { } - void MarkAsCall() { is_call_ = true; } + void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); } + bool IsCall() const { return IsCallBits::decode(bit_field_); } // Interface to the register allocator and iterators. - bool ClobbersTemps() const { return is_call_; } - bool ClobbersRegisters() const { return is_call_; } - bool ClobbersDoubleRegisters() const { return is_call_; } + bool ClobbersTemps() const { return IsCall(); } + bool ClobbersRegisters() const { return IsCall(); } + bool ClobbersDoubleRegisters() const { return IsCall(); } // Interface to the register allocator and iterators. - bool IsMarkedAsCall() const { return is_call_; } + bool IsMarkedAsCall() const { return IsCall(); } virtual bool HasResult() const = 0; virtual LOperand* result() const = 0; @@ -283,10 +295,13 @@ class LInstruction: public ZoneObject { virtual int TempCount() = 0; virtual LOperand* TempAt(int i) = 0; + class IsCallBits: public BitField {}; + class PositionBits: public BitField {}; + LEnvironment* environment_; SetOncePointer pointer_map_; HValue* hydrogen_value_; - bool is_call_; + int bit_field_; }; @@ -1489,19 +1504,6 @@ class LLoadNamedField: public LTemplateInstruction<1, 1, 0> { }; -class LLoadNamedFieldPolymorphic: public LTemplateInstruction<1, 1, 0> { - public: - explicit LLoadNamedFieldPolymorphic(LOperand* object) { - inputs_[0] = object; - } - - LOperand* object() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field-polymorphic") - DECLARE_HYDROGEN_ACCESSOR(LoadNamedFieldPolymorphic) -}; - - class LLoadNamedGeneric: public LTemplateInstruction<1, 1, 0> { public: explicit LLoadNamedGeneric(LOperand* object) { @@ -2690,7 +2692,8 @@ class LChunkBuilder BASE_EMBEDDED { CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY); LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env, - int* argument_index_accumulator); + int* argument_index_accumulator, + ZoneList* objects_to_materialize); void VisitInstruction(HInstruction* current); diff --git a/deps/v8/src/mips/macro-assembler-mips.cc b/deps/v8/src/mips/macro-assembler-mips.cc index a7ec713..e53f10a 100644 --- a/deps/v8/src/mips/macro-assembler-mips.cc +++ b/deps/v8/src/mips/macro-assembler-mips.cc @@ -2923,9 +2923,7 @@ void MacroAssembler::Allocate(int object_size, // Set up allocation top address and object size registers. Register topaddr = scratch1; - Register obj_size_reg = scratch2; li(topaddr, Operand(allocation_top)); - li(obj_size_reg, Operand(object_size)); // This code stores a temporary value in t9. if ((flags & RESULT_CONTAINS_TOP) == 0) { @@ -2944,9 +2942,23 @@ void MacroAssembler::Allocate(int object_size, lw(t9, MemOperand(topaddr, limit - top)); } + if ((flags & DOUBLE_ALIGNMENT) != 0) { + // Align the next allocation. Storing the filler map without checking top is + // always safe because the limit of the heap is always aligned. + ASSERT((flags & PRETENURE_OLD_POINTER_SPACE) == 0); + ASSERT(kPointerAlignment * 2 == kDoubleAlignment); + And(scratch2, result, Operand(kDoubleAlignmentMask)); + Label aligned; + Branch(&aligned, eq, scratch2, Operand(zero_reg)); + li(scratch2, Operand(isolate()->factory()->one_pointer_filler_map())); + sw(scratch2, MemOperand(result)); + Addu(result, result, Operand(kDoubleSize / 2)); + bind(&aligned); + } + // Calculate new top and bail out if new space is exhausted. Use result // to calculate the new top. - Addu(scratch2, result, Operand(obj_size_reg)); + Addu(scratch2, result, Operand(object_size)); Branch(gc_required, Ugreater, scratch2, Operand(t9)); sw(scratch2, MemOperand(topaddr)); @@ -3014,6 +3026,20 @@ void MacroAssembler::Allocate(Register object_size, lw(t9, MemOperand(topaddr, limit - top)); } + if ((flags & DOUBLE_ALIGNMENT) != 0) { + // Align the next allocation. Storing the filler map without checking top is + // always safe because the limit of the heap is always aligned. + ASSERT((flags & PRETENURE_OLD_POINTER_SPACE) == 0); + ASSERT(kPointerAlignment * 2 == kDoubleAlignment); + And(scratch2, result, Operand(kDoubleAlignmentMask)); + Label aligned; + Branch(&aligned, eq, scratch2, Operand(zero_reg)); + li(scratch2, Operand(isolate()->factory()->one_pointer_filler_map())); + sw(scratch2, MemOperand(result)); + Addu(result, result, Operand(kDoubleSize / 2)); + bind(&aligned); + } + // Calculate new top and bail out if new space is exhausted. Use result // to calculate the new top. Object size may be in words so a shift is // required to get the number of bytes. diff --git a/deps/v8/src/mips/macro-assembler-mips.h b/deps/v8/src/mips/macro-assembler-mips.h index ac37db2..61a0c3a 100644 --- a/deps/v8/src/mips/macro-assembler-mips.h +++ b/deps/v8/src/mips/macro-assembler-mips.h @@ -51,20 +51,6 @@ class JumpTarget; // MIPS generated code calls C code, it must be via t9 register. -// Flags used for the AllocateInNewSpace functions. -enum AllocationFlags { - // No special flags. - NO_ALLOCATION_FLAGS = 0, - // Return the pointer to the allocated already tagged as a heap object. - TAG_OBJECT = 1 << 0, - // The content of the result register already contains the allocation top in - // new space. - RESULT_CONTAINS_TOP = 1 << 1, - // Specify that the requested size of the space to allocate is specified in - // words instead of bytes. - SIZE_IN_WORDS = 1 << 2 -}; - // Flags used for AllocateHeapNumber enum TaggingMode { // Tag the result. diff --git a/deps/v8/src/objects-printer.cc b/deps/v8/src/objects-printer.cc index 87b2811..7b6f7a4 100644 --- a/deps/v8/src/objects-printer.cc +++ b/deps/v8/src/objects-printer.cc @@ -37,9 +37,6 @@ namespace internal { #ifdef OBJECT_PRINT -static const char* TypeToString(InstanceType type); - - void MaybeObject::Print() { Print(stdout); } @@ -509,83 +506,12 @@ void JSModule::JSModulePrint(FILE* out) { static const char* TypeToString(InstanceType type) { switch (type) { - case INVALID_TYPE: return "INVALID"; - case MAP_TYPE: return "MAP"; - case HEAP_NUMBER_TYPE: return "HEAP_NUMBER"; - case SYMBOL_TYPE: return "SYMBOL"; - case STRING_TYPE: return "TWO_BYTE_STRING"; - case ASCII_STRING_TYPE: return "ASCII_STRING"; - case CONS_STRING_TYPE: - case CONS_ASCII_STRING_TYPE: - return "CONS_STRING"; - case EXTERNAL_STRING_TYPE: - case EXTERNAL_ASCII_STRING_TYPE: - case EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE: - return "EXTERNAL_STRING"; - case SHORT_EXTERNAL_STRING_TYPE: - case SHORT_EXTERNAL_ASCII_STRING_TYPE: - case SHORT_EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE: - return "SHORT_EXTERNAL_STRING"; - case INTERNALIZED_STRING_TYPE: return "INTERNALIZED_STRING"; - case ASCII_INTERNALIZED_STRING_TYPE: return "ASCII_INTERNALIZED_STRING"; - case CONS_INTERNALIZED_STRING_TYPE: return "CONS_INTERNALIZED_STRING"; - case CONS_ASCII_INTERNALIZED_STRING_TYPE: - return "CONS_ASCII_INTERNALIZED_STRING"; - case EXTERNAL_INTERNALIZED_STRING_TYPE: - case EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE: - case EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE: - return "EXTERNAL_INTERNALIZED_STRING"; - case SHORT_EXTERNAL_INTERNALIZED_STRING_TYPE: - case SHORT_EXTERNAL_ASCII_INTERNALIZED_STRING_TYPE: - case SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE: - return "SHORT_EXTERNAL_INTERNALIZED_STRING"; - case FIXED_ARRAY_TYPE: return "FIXED_ARRAY"; - case BYTE_ARRAY_TYPE: return "BYTE_ARRAY"; - case FREE_SPACE_TYPE: return "FREE_SPACE"; - case EXTERNAL_PIXEL_ARRAY_TYPE: return "EXTERNAL_PIXEL_ARRAY"; - case EXTERNAL_BYTE_ARRAY_TYPE: return "EXTERNAL_BYTE_ARRAY"; - case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: - return "EXTERNAL_UNSIGNED_BYTE_ARRAY"; - case EXTERNAL_SHORT_ARRAY_TYPE: return "EXTERNAL_SHORT_ARRAY"; - case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: - return "EXTERNAL_UNSIGNED_SHORT_ARRAY"; - case EXTERNAL_INT_ARRAY_TYPE: return "EXTERNAL_INT_ARRAY"; - case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: - return "EXTERNAL_UNSIGNED_INT_ARRAY"; - case EXTERNAL_FLOAT_ARRAY_TYPE: return "EXTERNAL_FLOAT_ARRAY"; - case EXTERNAL_DOUBLE_ARRAY_TYPE: return "EXTERNAL_DOUBLE_ARRAY"; - case FILLER_TYPE: return "FILLER"; - case JS_OBJECT_TYPE: return "JS_OBJECT"; - case JS_CONTEXT_EXTENSION_OBJECT_TYPE: return "JS_CONTEXT_EXTENSION_OBJECT"; - case ODDBALL_TYPE: return "ODDBALL"; - case CELL_TYPE: return "CELL"; - case PROPERTY_CELL_TYPE: return "PROPERTY_CELL"; - case SHARED_FUNCTION_INFO_TYPE: return "SHARED_FUNCTION_INFO"; - case JS_GENERATOR_OBJECT_TYPE: return "JS_GENERATOR_OBJECT"; - case JS_MODULE_TYPE: return "JS_MODULE"; - case JS_FUNCTION_TYPE: return "JS_FUNCTION"; - case CODE_TYPE: return "CODE"; - case JS_ARRAY_TYPE: return "JS_ARRAY"; - case JS_PROXY_TYPE: return "JS_PROXY"; - case JS_SET_TYPE: return "JS_SET"; - case JS_MAP_TYPE: return "JS_MAP"; - case JS_WEAK_MAP_TYPE: return "JS_WEAK_MAP"; - case JS_WEAK_SET_TYPE: return "JS_WEAK_SET"; - case JS_REGEXP_TYPE: return "JS_REGEXP"; - case JS_VALUE_TYPE: return "JS_VALUE"; - case JS_GLOBAL_OBJECT_TYPE: return "JS_GLOBAL_OBJECT"; - case JS_BUILTINS_OBJECT_TYPE: return "JS_BUILTINS_OBJECT"; - case JS_GLOBAL_PROXY_TYPE: return "JS_GLOBAL_PROXY"; - case JS_ARRAY_BUFFER_TYPE: return "JS_ARRAY_BUFFER"; - case JS_TYPED_ARRAY_TYPE: return "JS_TYPED_ARRAY"; - case JS_DATA_VIEW_TYPE: return "JS_DATA_VIEW"; - case FOREIGN_TYPE: return "FOREIGN"; - case JS_MESSAGE_OBJECT_TYPE: return "JS_MESSAGE_OBJECT_TYPE"; -#define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: return #NAME; - STRUCT_LIST(MAKE_STRUCT_CASE) -#undef MAKE_STRUCT_CASE - default: return "UNKNOWN"; +#define TYPE_TO_STRING(TYPE) case TYPE: return #TYPE; + INSTANCE_TYPE_LIST(TYPE_TO_STRING) +#undef TYPE_TO_STRING } + UNREACHABLE(); + return "UNKNOWN"; // Keep the compiler happy. } diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 734bf40..995ed36 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -9222,6 +9222,7 @@ void JSFunction::MarkForLazyRecompilation() { ASSERT(!IsOptimized()); ASSERT(shared()->allows_lazy_compilation() || code()->optimizable()); + ASSERT(!shared()->is_generator()); set_code_no_write_barrier( GetIsolate()->builtins()->builtin(Builtins::kLazyRecompile)); // No write barrier required, since the builtin is part of the root set. @@ -9232,10 +9233,8 @@ void JSFunction::MarkForParallelRecompilation() { ASSERT(is_compiled() || GetIsolate()->DebuggerHasBreakPoints()); ASSERT(!IsOptimized()); ASSERT(shared()->allows_lazy_compilation() || code()->optimizable()); - if (!FLAG_parallel_recompilation) { - JSFunction::MarkForLazyRecompilation(); - return; - } + ASSERT(!shared()->is_generator()); + ASSERT(FLAG_parallel_recompilation); if (FLAG_trace_parallel_recompilation) { PrintF(" ** Marking "); PrintName(); @@ -10637,7 +10636,14 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) { break; } - case Translation::ARGUMENTS_OBJECT: { + case Translation::DUPLICATED_OBJECT: { + int object_index = iterator.Next(); + PrintF(out, "{object_index=%d}", object_index); + break; + } + + case Translation::ARGUMENTS_OBJECT: + case Translation::CAPTURED_OBJECT: { int args_length = iterator.Next(); PrintF(out, "{length=%d}", args_length); break; @@ -11348,6 +11354,7 @@ bool DependentCode::Contains(DependencyGroup group, Code* code) { void DependentCode::DeoptimizeDependentCodeGroup( Isolate* isolate, DependentCode::DependencyGroup group) { + ASSERT(AllowCodeDependencyChange::IsAllowed()); DisallowHeapAllocation no_allocation_scope; DependentCode::GroupStartIndexes starts(this); int start = starts.at(group); diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index b2dc181..f800c5d 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -333,6 +333,7 @@ const int kStubMinorKeyBits = kBitsPerInt - kSmiTagSize - kStubMajorKeyBits; V(CONS_STRING_TYPE) \ V(CONS_ASCII_STRING_TYPE) \ V(SLICED_STRING_TYPE) \ + V(SLICED_ASCII_STRING_TYPE) \ V(EXTERNAL_STRING_TYPE) \ V(EXTERNAL_ASCII_STRING_TYPE) \ V(EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE) \ @@ -416,6 +417,8 @@ const int kStubMinorKeyBits = kBitsPerInt - kSmiTagSize - kStubMajorKeyBits; V(JS_TYPED_ARRAY_TYPE) \ V(JS_DATA_VIEW_TYPE) \ V(JS_PROXY_TYPE) \ + V(JS_SET_TYPE) \ + V(JS_MAP_TYPE) \ V(JS_WEAK_MAP_TYPE) \ V(JS_WEAK_SET_TYPE) \ V(JS_REGEXP_TYPE) \ @@ -783,7 +786,6 @@ enum InstanceType { // Pseudo-types FIRST_TYPE = 0x0, LAST_TYPE = JS_FUNCTION_TYPE, - INVALID_TYPE = FIRST_TYPE - 1, FIRST_NAME_TYPE = FIRST_TYPE, LAST_NAME_TYPE = SYMBOL_TYPE, FIRST_UNIQUE_NAME_TYPE = INTERNALIZED_STRING_TYPE, @@ -1074,7 +1076,7 @@ class MaybeObject BASE_EMBEDDED { "bad value context for arguments object value") \ V(kBadValueContextForArgumentsValue, \ "bad value context for arguments value") \ - V(kBailedOutDueToDependentMap, "bailed out due to dependent map") \ + V(kBailedOutDueToDependencyChange, "bailed out due to dependency change") \ V(kBailoutWasNotPrepared, "bailout was not prepared") \ V(kBinaryStubGenerateFloatingPointCode, \ "BinaryStub_GenerateFloatingPointCode") \ diff --git a/deps/v8/src/optimizing-compiler-thread.cc b/deps/v8/src/optimizing-compiler-thread.cc index 21ef237..337ddd4 100644 --- a/deps/v8/src/optimizing-compiler-thread.cc +++ b/deps/v8/src/optimizing-compiler-thread.cc @@ -60,12 +60,25 @@ void OptimizingCompilerThread::Run() { OS::Sleep(FLAG_parallel_recompilation_delay); } - if (Acquire_Load(&stop_thread_)) { - stop_semaphore_->Signal(); - if (FLAG_trace_parallel_recompilation) { - time_spent_total_ = OS::Ticks() - epoch; - } - return; + switch (static_cast(Acquire_Load(&stop_thread_))) { + case CONTINUE: + break; + case STOP: + if (FLAG_trace_parallel_recompilation) { + time_spent_total_ = OS::Ticks() - epoch; + } + stop_semaphore_->Signal(); + return; + case FLUSH: + // The main thread is blocked, waiting for the stop semaphore. + { AllowHandleDereference allow_handle_dereference; + FlushInputQueue(true); + } + Release_Store(&queue_length_, static_cast(0)); + Release_Store(&stop_thread_, static_cast(CONTINUE)); + stop_semaphore_->Signal(); + // Return to start of consumer loop. + continue; } int64_t compiling_start = 0; @@ -82,7 +95,9 @@ void OptimizingCompilerThread::Run() { void OptimizingCompilerThread::CompileNext() { OptimizingCompiler* optimizing_compiler = NULL; - input_queue_.Dequeue(&optimizing_compiler); + bool result = input_queue_.Dequeue(&optimizing_compiler); + USE(result); + ASSERT(result); Barrier_AtomicIncrement(&queue_length_, static_cast(-1)); // The function may have already been optimized by OSR. Simply continue. @@ -102,26 +117,61 @@ void OptimizingCompilerThread::CompileNext() { } +void OptimizingCompilerThread::FlushInputQueue(bool restore_function_code) { + OptimizingCompiler* optimizing_compiler; + // The optimizing compiler is allocated in the CompilationInfo's zone. + while (input_queue_.Dequeue(&optimizing_compiler)) { + // This should not block, since we have one signal on the input queue + // semaphore corresponding to each element in the input queue. + input_queue_semaphore_->Wait(); + CompilationInfo* info = optimizing_compiler->info(); + if (restore_function_code) { + Handle function = info->closure(); + function->ReplaceCode(function->shared()->code()); + } + delete info; + } +} + + +void OptimizingCompilerThread::FlushOutputQueue(bool restore_function_code) { + OptimizingCompiler* optimizing_compiler; + // The optimizing compiler is allocated in the CompilationInfo's zone. + while (output_queue_.Dequeue(&optimizing_compiler)) { + CompilationInfo* info = optimizing_compiler->info(); + if (restore_function_code) { + Handle function = info->closure(); + function->ReplaceCode(function->shared()->code()); + } + delete info; + } +} + + +void OptimizingCompilerThread::Flush() { + ASSERT(!IsOptimizerThread()); + Release_Store(&stop_thread_, static_cast(FLUSH)); + input_queue_semaphore_->Signal(); + stop_semaphore_->Wait(); + FlushOutputQueue(true); +} + + void OptimizingCompilerThread::Stop() { ASSERT(!IsOptimizerThread()); - Release_Store(&stop_thread_, static_cast(true)); + Release_Store(&stop_thread_, static_cast(STOP)); input_queue_semaphore_->Signal(); stop_semaphore_->Wait(); if (FLAG_parallel_recompilation_delay != 0) { // Barrier when loading queue length is not necessary since the write // happens in CompileNext on the same thread. + // This is used only for testing. while (NoBarrier_Load(&queue_length_) > 0) CompileNext(); InstallOptimizedFunctions(); } else { - OptimizingCompiler* optimizing_compiler; - // The optimizing compiler is allocated in the CompilationInfo's zone. - while (input_queue_.Dequeue(&optimizing_compiler)) { - delete optimizing_compiler->info(); - } - while (output_queue_.Dequeue(&optimizing_compiler)) { - delete optimizing_compiler->info(); - } + FlushInputQueue(false); + FlushOutputQueue(false); } if (FLAG_trace_parallel_recompilation) { diff --git a/deps/v8/src/optimizing-compiler-thread.h b/deps/v8/src/optimizing-compiler-thread.h index 275ceb4..cbd4d0e 100644 --- a/deps/v8/src/optimizing-compiler-thread.h +++ b/deps/v8/src/optimizing-compiler-thread.h @@ -54,13 +54,13 @@ class OptimizingCompilerThread : public Thread { install_mutex_(OS::CreateMutex()), time_spent_compiling_(0), time_spent_total_(0) { - NoBarrier_Store(&stop_thread_, static_cast(false)); + NoBarrier_Store(&stop_thread_, static_cast(CONTINUE)); NoBarrier_Store(&queue_length_, static_cast(0)); } void Run(); void Stop(); - void CompileNext(); + void Flush(); void QueueForOptimization(OptimizingCompiler* optimizing_compiler); void InstallOptimizedFunctions(); @@ -92,6 +92,13 @@ class OptimizingCompilerThread : public Thread { } private: + enum StopFlag { CONTINUE, STOP, FLUSH }; + + void FlushInputQueue(bool restore_function_code); + void FlushOutputQueue(bool restore_function_code); + + void CompileNext(); + #ifdef DEBUG int thread_id_; Mutex* thread_id_mutex_; diff --git a/deps/v8/src/profile-generator-inl.h b/deps/v8/src/profile-generator-inl.h index d92085a..6791c88 100644 --- a/deps/v8/src/profile-generator-inl.h +++ b/deps/v8/src/profile-generator-inl.h @@ -92,6 +92,8 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { case OTHER: case EXTERNAL: return program_entry_; + case IDLE: + return idle_entry_; default: return NULL; } } diff --git a/deps/v8/src/profile-generator.cc b/deps/v8/src/profile-generator.cc index e772a54..86bd17b 100644 --- a/deps/v8/src/profile-generator.cc +++ b/deps/v8/src/profile-generator.cc @@ -220,7 +220,7 @@ double ProfileNode::GetTotalMillis() const { void ProfileNode::Print(int indent) { - OS::Print("%5u %5u %*c %s%s #%d %d", + OS::Print("%5u %5u %*c %s%s %d #%d", total_ticks_, self_ticks_, indent, ' ', entry_->name_prefix(), @@ -614,6 +614,8 @@ const char* const ProfileGenerator::kAnonymousFunctionName = "(anonymous function)"; const char* const ProfileGenerator::kProgramEntryName = "(program)"; +const char* const ProfileGenerator::kIdleEntryName = + "(idle)"; const char* const ProfileGenerator::kGarbageCollectorEntryName = "(garbage collector)"; const char* const ProfileGenerator::kUnresolvedFunctionName = @@ -624,6 +626,8 @@ ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles) : profiles_(profiles), program_entry_( profiles->NewCodeEntry(Logger::FUNCTION_TAG, kProgramEntryName)), + idle_entry_( + profiles->NewCodeEntry(Logger::FUNCTION_TAG, kIdleEntryName)), gc_entry_( profiles->NewCodeEntry(Logger::BUILTIN_TAG, kGarbageCollectorEntryName)), diff --git a/deps/v8/src/profile-generator.h b/deps/v8/src/profile-generator.h index 0cc397e..99aeb1f 100644 --- a/deps/v8/src/profile-generator.h +++ b/deps/v8/src/profile-generator.h @@ -342,6 +342,7 @@ class ProfileGenerator { static const char* const kAnonymousFunctionName; static const char* const kProgramEntryName; + static const char* const kIdleEntryName; static const char* const kGarbageCollectorEntryName; // Used to represent frames for which we have no reliable way to // detect function. @@ -353,6 +354,7 @@ class ProfileGenerator { CpuProfilesCollection* profiles_; CodeMap code_map_; CodeEntry* program_entry_; + CodeEntry* idle_entry_; CodeEntry* gc_entry_; CodeEntry* unresolved_entry_; diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index 0916b93..10de6f9 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -71,7 +71,10 @@ #include "unicode/brkiter.h" #include "unicode/calendar.h" #include "unicode/coll.h" +#include "unicode/curramt.h" #include "unicode/datefmt.h" +#include "unicode/dcfmtsym.h" +#include "unicode/decimfmt.h" #include "unicode/dtfmtsym.h" #include "unicode/dtptngen.h" #include "unicode/locid.h" @@ -79,7 +82,11 @@ #include "unicode/numsys.h" #include "unicode/smpdtfmt.h" #include "unicode/timezone.h" +#include "unicode/uchar.h" +#include "unicode/ucol.h" +#include "unicode/ucurr.h" #include "unicode/uloc.h" +#include "unicode/unum.h" #include "unicode/uversion.h" #endif @@ -3013,6 +3020,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ResumeJSGeneratorObject) { JavaScriptFrame* frame = stack_iterator.frame(); ASSERT_EQ(frame->function(), generator_object->function()); + ASSERT(frame->function()->is_compiled()); STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting <= 0); STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed <= 0); @@ -8487,8 +8495,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOptimizationStatus) { } CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); if (FLAG_parallel_recompilation && sync_with_compiler_thread) { - while (function->IsMarkedForParallelRecompilation() || - function->IsInRecompileQueue() || + while (function->IsInRecompileQueue() || function->IsMarkedForInstallingRecompiledCode()) { isolate->optimizing_compiler_thread()->InstallOptimizedFunctions(); OS::Sleep(50); @@ -11220,6 +11227,7 @@ static Handle MaterializeStackLocalsWithFrameInspector( ? frame_inspector->GetParameter(i) : isolate->heap()->undefined_value(), isolate); + ASSERT(!value->IsTheHole()); RETURN_IF_EMPTY_HANDLE_VALUE( isolate, @@ -11234,12 +11242,15 @@ static Handle MaterializeStackLocalsWithFrameInspector( // Second fill all stack locals. for (int i = 0; i < scope_info->StackLocalCount(); ++i) { + Handle value(frame_inspector->GetExpression(i), isolate); + if (value->IsTheHole()) continue; + RETURN_IF_EMPTY_HANDLE_VALUE( isolate, SetProperty(isolate, target, Handle(scope_info->StackLocalName(i)), - Handle(frame_inspector->GetExpression(i), isolate), + value, NONE, kNonStrictMode), Handle()); @@ -11266,6 +11277,7 @@ static void UpdateStackLocalsFromMaterializedObject(Isolate* isolate, // Parameters. for (int i = 0; i < scope_info->ParameterCount(); ++i) { + ASSERT(!frame->GetParameter(i)->IsTheHole()); HandleScope scope(isolate); Handle value = GetProperty( isolate, target, Handle(scope_info->ParameterName(i))); @@ -11274,6 +11286,7 @@ static void UpdateStackLocalsFromMaterializedObject(Isolate* isolate, // Stack locals. for (int i = 0; i < scope_info->StackLocalCount(); ++i) { + if (frame->GetExpression(i)->IsTheHole()) continue; HandleScope scope(isolate); Handle value = GetProperty( isolate, target, Handle(scope_info->StackLocalName(i))); @@ -12008,8 +12021,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetStepInPositions) { JavaScriptFrameIterator frame_it(isolate, id); JavaScriptFrame* frame = frame_it.frame(); + Handle fun = + Handle(frame->function()); Handle shared = - Handle(frame->function()->shared()); + Handle(fun->shared()); + + if (!isolate->debug()->EnsureDebugInfo(shared, fun)) { + return isolate->heap()->undefined_value(); + } + Handle debug_info = Debug::GetDebugInfo(shared); int len = 0; @@ -12022,12 +12042,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetStepInPositions) { int current_statement_pos = break_location_iterator.statement_position(); while (!break_location_iterator.Done()) { - if (break_location_iterator.IsStepInLocation(isolate)) { - Smi* position_value = Smi::FromInt(break_location_iterator.position()); - JSObject::SetElement(array, len, - Handle(position_value, isolate), - NONE, kNonStrictMode); - len++; + if (break_location_iterator.pc() > frame->pc()) { + if (break_location_iterator.IsStepInLocation(isolate)) { + Smi* position_value = Smi::FromInt(break_location_iterator.position()); + JSObject::SetElement(array, len, + Handle(position_value, isolate), + NONE, kNonStrictMode); + len++; + } } // Advance iterator. break_location_iterator.Next(); @@ -13603,9 +13625,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateDateTimeFormat) { v8::Utils::ToLocal(local_object)); // Make object handle weak so we can delete the data format once GC kicks in. wrapper.MakeWeak(NULL, &DateFormat::DeleteDateFormat); - Handle result = Utils::OpenPersistent(wrapper); wrapper.ClearAndLeak(); - return *result; + return *local_object; } @@ -13665,6 +13686,192 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalDateParse) { } return *result; } + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateNumberFormat) { + HandleScope scope(isolate); + + ASSERT(args.length() == 3); + + CONVERT_ARG_HANDLE_CHECKED(String, locale, 0); + CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1); + CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2); + + Handle number_format_template = + I18N::GetTemplate(isolate); + + // Create an empty object wrapper. + bool has_pending_exception = false; + Handle local_object = Execution::InstantiateObject( + number_format_template, &has_pending_exception); + if (has_pending_exception) { + ASSERT(isolate->has_pending_exception()); + return Failure::Exception(); + } + + // Set number formatter as internal field of the resulting JS object. + icu::DecimalFormat* number_format = NumberFormat::InitializeNumberFormat( + isolate, locale, options, resolved); + + if (!number_format) return isolate->ThrowIllegalOperation(); + + local_object->SetInternalField(0, reinterpret_cast(number_format)); + + RETURN_IF_EMPTY_HANDLE(isolate, + JSObject::SetLocalPropertyIgnoreAttributes( + local_object, + isolate->factory()->NewStringFromAscii(CStrVector("numberFormat")), + isolate->factory()->NewStringFromAscii(CStrVector("valid")), + NONE)); + + Persistent wrapper(reinterpret_cast(isolate), + v8::Utils::ToLocal(local_object)); + // Make object handle weak so we can delete the number format once GC kicks + // in. + wrapper.MakeWeak(NULL, &NumberFormat::DeleteNumberFormat); + wrapper.ClearAndLeak(); + return *local_object; +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalNumberFormat) { + HandleScope scope(isolate); + + ASSERT(args.length() == 2); + + CONVERT_ARG_HANDLE_CHECKED(JSObject, number_format_holder, 0); + CONVERT_ARG_HANDLE_CHECKED(Object, number, 1); + + bool has_pending_exception = false; + double value = Execution::ToNumber(number, &has_pending_exception)->Number(); + if (has_pending_exception) { + ASSERT(isolate->has_pending_exception()); + return Failure::Exception(); + } + + icu::DecimalFormat* number_format = + NumberFormat::UnpackNumberFormat(isolate, number_format_holder); + if (!number_format) return isolate->ThrowIllegalOperation(); + + icu::UnicodeString result; + number_format->format(value, result); + + return *isolate->factory()->NewStringFromTwoByte( + Vector( + reinterpret_cast(result.getBuffer()), + result.length())); +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalNumberParse) { + HandleScope scope(isolate); + + ASSERT(args.length() == 2); + + CONVERT_ARG_HANDLE_CHECKED(JSObject, number_format_holder, 0); + CONVERT_ARG_HANDLE_CHECKED(String, number_string, 1); + + v8::String::Utf8Value utf8_number(v8::Utils::ToLocal(number_string)); + icu::UnicodeString u_number(icu::UnicodeString::fromUTF8(*utf8_number)); + icu::DecimalFormat* number_format = + NumberFormat::UnpackNumberFormat(isolate, number_format_holder); + if (!number_format) return isolate->ThrowIllegalOperation(); + + UErrorCode status = U_ZERO_ERROR; + icu::Formattable result; + // ICU 4.6 doesn't support parseCurrency call. We need to wait for ICU49 + // to be part of Chrome. + // TODO(cira): Include currency parsing code using parseCurrency call. + // We need to check if the formatter parses all currencies or only the + // one it was constructed with (it will impact the API - how to return ISO + // code and the value). + number_format->parse(u_number, result, status); + if (U_FAILURE(status)) return isolate->heap()->undefined_value(); + + switch (result.getType()) { + case icu::Formattable::kDouble: + return *isolate->factory()->NewNumber(result.getDouble()); + case icu::Formattable::kLong: + return *isolate->factory()->NewNumberFromInt(result.getLong()); + case icu::Formattable::kInt64: + return *isolate->factory()->NewNumber( + static_cast(result.getInt64())); + default: + return isolate->heap()->undefined_value(); + } +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateCollator) { + HandleScope scope(isolate); + + ASSERT(args.length() == 3); + + CONVERT_ARG_HANDLE_CHECKED(String, locale, 0); + CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1); + CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2); + + Handle collator_template = I18N::GetTemplate(isolate); + + // Create an empty object wrapper. + bool has_pending_exception = false; + Handle local_object = Execution::InstantiateObject( + collator_template, &has_pending_exception); + if (has_pending_exception) { + ASSERT(isolate->has_pending_exception()); + return Failure::Exception(); + } + + // Set collator as internal field of the resulting JS object. + icu::Collator* collator = Collator::InitializeCollator( + isolate, locale, options, resolved); + + if (!collator) return isolate->ThrowIllegalOperation(); + + local_object->SetInternalField(0, reinterpret_cast(collator)); + + RETURN_IF_EMPTY_HANDLE(isolate, + JSObject::SetLocalPropertyIgnoreAttributes( + local_object, + isolate->factory()->NewStringFromAscii(CStrVector("collator")), + isolate->factory()->NewStringFromAscii(CStrVector("valid")), + NONE)); + + Persistent wrapper(reinterpret_cast(isolate), + v8::Utils::ToLocal(local_object)); + // Make object handle weak so we can delete the collator once GC kicks in. + wrapper.MakeWeak(NULL, &Collator::DeleteCollator); + wrapper.ClearAndLeak(); + return *local_object; +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalCompare) { + HandleScope scope(isolate); + + ASSERT(args.length() == 3); + + CONVERT_ARG_HANDLE_CHECKED(JSObject, collator_holder, 0); + CONVERT_ARG_HANDLE_CHECKED(String, string1, 1); + CONVERT_ARG_HANDLE_CHECKED(String, string2, 2); + + icu::Collator* collator = Collator::UnpackCollator(isolate, collator_holder); + if (!collator) return isolate->ThrowIllegalOperation(); + + v8::String::Value string_value1(v8::Utils::ToLocal(string1)); + v8::String::Value string_value2(v8::Utils::ToLocal(string2)); + const UChar* u_string1 = reinterpret_cast(*string_value1); + const UChar* u_string2 = reinterpret_cast(*string_value2); + UErrorCode status = U_ZERO_ERROR; + UCollationResult result = collator->compare(u_string1, + string_value1.length(), + u_string2, + string_value2.length(), + status); + if (U_FAILURE(status)) return isolate->ThrowIllegalOperation(); + + return *isolate->factory()->NewNumberFromInt(result); +} #endif // V8_I18N_SUPPORT diff --git a/deps/v8/src/runtime.h b/deps/v8/src/runtime.h index ade7e73..23e91f2 100644 --- a/deps/v8/src/runtime.h +++ b/deps/v8/src/runtime.h @@ -548,6 +548,15 @@ namespace internal { F(CreateDateTimeFormat, 3, 1) \ F(InternalDateFormat, 2, 1) \ F(InternalDateParse, 2, 1) \ + \ + /* Number format and parse. */ \ + F(CreateNumberFormat, 3, 1) \ + F(InternalNumberFormat, 2, 1) \ + F(InternalNumberParse, 2, 1) \ + \ + /* Collator. */ \ + F(CreateCollator, 3, 1) \ + F(InternalCompare, 3, 1) \ #else #define RUNTIME_FUNCTION_LIST_I18N_SUPPORT(F) diff --git a/deps/v8/src/sampler.cc b/deps/v8/src/sampler.cc index 6e451f0..1d0cded 100644 --- a/deps/v8/src/sampler.cc +++ b/deps/v8/src/sampler.cc @@ -619,8 +619,7 @@ DISABLE_ASAN void TickSample::Init(Isolate* isolate, // Avoid collecting traces while doing GC. if (state == GC) return; - const Address js_entry_sp = - Isolate::js_entry_sp(isolate->thread_local_top()); + Address js_entry_sp = isolate->js_entry_sp(); if (js_entry_sp == 0) { // Not executing JS now. return; diff --git a/deps/v8/src/splay-tree-inl.h b/deps/v8/src/splay-tree-inl.h index 4eca71d..4202475 100644 --- a/deps/v8/src/splay-tree-inl.h +++ b/deps/v8/src/splay-tree-inl.h @@ -91,6 +91,12 @@ bool SplayTree::FindInternal(const Key& key) { template +bool SplayTree::Contains(const Key& key) { + return FindInternal(key); +} + + +template bool SplayTree::Find(const Key& key, Locator* locator) { if (FindInternal(key)) { locator->bind(root_); @@ -293,9 +299,10 @@ void SplayTree::ForEach(Callback* callback) { template template void SplayTree::ForEachNode(Callback* callback) { + if (root_ == NULL) return; // Pre-allocate some space for tiny trees. List nodes_to_visit(10, allocator_); - if (root_ != NULL) nodes_to_visit.Add(root_, allocator_); + nodes_to_visit.Add(root_, allocator_); int pos = 0; while (pos < nodes_to_visit.length()) { Node* node = nodes_to_visit[pos++]; diff --git a/deps/v8/src/splay-tree.h b/deps/v8/src/splay-tree.h index 8844d8a..f393027 100644 --- a/deps/v8/src/splay-tree.h +++ b/deps/v8/src/splay-tree.h @@ -39,9 +39,9 @@ namespace internal { // // typedef Key: the key type // typedef Value: the value type -// static const kNoKey: the dummy key used when no key is set -// static const kNoValue: the dummy value used to initialize nodes -// int (Compare)(Key& a, Key& b) -> {-1, 0, 1}: comparison function +// static const Key kNoKey: the dummy key used when no key is set +// static Value kNoValue(): the dummy value used to initialize nodes +// static int (Compare)(Key& a, Key& b) -> {-1, 0, 1}: comparison function // // The tree is also parameterized by an allocation policy // (Allocator). The policy is used for allocating lists in the C free @@ -74,6 +74,11 @@ class SplayTree { UNREACHABLE(); } + AllocationPolicy allocator() { return allocator_; } + + // Checks if there is a mapping for the key. + bool Contains(const Key& key); + // Inserts the given key in this tree with the given value. Returns // true if a node was inserted, otherwise false. If found the locator // is enabled and provides access to the mapping for the key. @@ -104,6 +109,9 @@ class SplayTree { // Remove the node with the given key from the tree. bool Remove(const Key& key); + // Remove all keys from the tree. + void Clear() { ResetRoot(); } + bool is_empty() { return root_ == NULL; } // Perform the splay operation for the given key. Moves the node with diff --git a/deps/v8/src/types.h b/deps/v8/src/types.h index b2eb60c..fc69c78 100644 --- a/deps/v8/src/types.h +++ b/deps/v8/src/types.h @@ -303,6 +303,11 @@ struct Bounds { explicit Bounds(Handle t) : lower(t), upper(t) {} Bounds(Type* t, Isolate* isl) : lower(t, isl), upper(t, isl) {} + // Unrestricted bounds. + static Bounds Unbounded(Isolate* isl) { + return Bounds(Type::None(), Type::Any(), isl); + } + // Meet: both b1 and b2 are known to hold. static Bounds Both(Bounds b1, Bounds b2, Isolate* isl) { return Bounds( diff --git a/deps/v8/src/typing.cc b/deps/v8/src/typing.cc index 727c104..f8e2a7c 100644 --- a/deps/v8/src/typing.cc +++ b/deps/v8/src/typing.cc @@ -40,7 +40,8 @@ AstTyper::AstTyper(CompilationInfo* info) Handle(info->closure()->shared()->code()), Handle(info->closure()->context()->native_context()), info->isolate(), - info->zone()) { + info->zone()), + store_(info->zone()) { InitializeAstVisitor(); } @@ -79,12 +80,16 @@ void AstTyper::VisitStatements(ZoneList* stmts) { for (int i = 0; i < stmts->length(); ++i) { Statement* stmt = stmts->at(i); RECURSE(Visit(stmt)); + if (stmt->IsJump()) break; } } void AstTyper::VisitBlock(Block* stmt) { RECURSE(VisitStatements(stmt->statements())); + if (stmt->labels() != NULL) { + store_.Forget(); // Control may transfer here via 'break l'. + } } @@ -98,30 +103,41 @@ void AstTyper::VisitEmptyStatement(EmptyStatement* stmt) { void AstTyper::VisitIfStatement(IfStatement* stmt) { - RECURSE(Visit(stmt->condition())); - RECURSE(Visit(stmt->then_statement())); - RECURSE(Visit(stmt->else_statement())); - + // Collect type feedback. if (!stmt->condition()->ToBooleanIsTrue() && !stmt->condition()->ToBooleanIsFalse()) { stmt->condition()->RecordToBooleanTypeFeedback(oracle()); } + + RECURSE(Visit(stmt->condition())); + Effects then_effects = EnterEffects(); + RECURSE(Visit(stmt->then_statement())); + ExitEffects(); + Effects else_effects = EnterEffects(); + RECURSE(Visit(stmt->else_statement())); + ExitEffects(); + then_effects.Alt(else_effects); + store_.Seq(then_effects); } void AstTyper::VisitContinueStatement(ContinueStatement* stmt) { + // TODO(rossberg): is it worth having a non-termination effect? } void AstTyper::VisitBreakStatement(BreakStatement* stmt) { + // TODO(rossberg): is it worth having a non-termination effect? } void AstTyper::VisitReturnStatement(ReturnStatement* stmt) { - RECURSE(Visit(stmt->expression())); - + // Collect type feedback. // TODO(rossberg): we only need this for inlining into test contexts... stmt->expression()->RecordToBooleanTypeFeedback(oracle()); + + RECURSE(Visit(stmt->expression())); + // TODO(rossberg): is it worth having a non-termination effect? } @@ -133,14 +149,18 @@ void AstTyper::VisitWithStatement(WithStatement* stmt) { void AstTyper::VisitSwitchStatement(SwitchStatement* stmt) { RECURSE(Visit(stmt->tag())); + ZoneList* clauses = stmt->cases(); SwitchStatement::SwitchType switch_type = stmt->switch_type(); + Effects local_effects(zone()); + bool complex_effects = false; // True for label effects or fall-through. + for (int i = 0; i < clauses->length(); ++i) { CaseClause* clause = clauses->at(i); + Effects clause_effects = EnterEffects(); + if (!clause->is_default()) { Expression* label = clause->label(); - RECURSE(Visit(label)); - SwitchStatement::SwitchType label_switch_type = label->IsSmiLiteral() ? SwitchStatement::SMI_SWITCH : label->IsStringLiteral() ? SwitchStatement::STRING_SWITCH : @@ -149,13 +169,32 @@ void AstTyper::VisitSwitchStatement(SwitchStatement* stmt) { switch_type = label_switch_type; else if (switch_type != label_switch_type) switch_type = SwitchStatement::GENERIC_SWITCH; + + RECURSE(Visit(label)); + if (!clause_effects.IsEmpty()) complex_effects = true; + } + + ZoneList* stmts = clause->statements(); + RECURSE(VisitStatements(stmts)); + ExitEffects(); + if (stmts->is_empty() || stmts->last()->IsJump()) { + local_effects.Alt(clause_effects); + } else { + complex_effects = true; } - RECURSE(VisitStatements(clause->statements())); } + + if (complex_effects) { + store_.Forget(); // Reached this in unknown state. + } else { + store_.Seq(local_effects); + } + if (switch_type == SwitchStatement::UNKNOWN_SWITCH) switch_type = SwitchStatement::GENERIC_SWITCH; stmt->set_switch_type(switch_type); + // Collect type feedback. // TODO(rossberg): can we eliminate this special case and extra loop? if (switch_type == SwitchStatement::SMI_SWITCH) { for (int i = 0; i < clauses->length(); ++i) { @@ -168,22 +207,31 @@ void AstTyper::VisitSwitchStatement(SwitchStatement* stmt) { void AstTyper::VisitDoWhileStatement(DoWhileStatement* stmt) { - RECURSE(Visit(stmt->body())); - RECURSE(Visit(stmt->cond())); - + // Collect type feedback. if (!stmt->cond()->ToBooleanIsTrue()) { stmt->cond()->RecordToBooleanTypeFeedback(oracle()); } + + // TODO(rossberg): refine the unconditional Forget (here and elsewhere) by + // computing the set of variables assigned in only some of the origins of the + // control transfer (such as the loop body here). + store_.Forget(); // Control may transfer here via looping or 'continue'. + RECURSE(Visit(stmt->body())); + RECURSE(Visit(stmt->cond())); + store_.Forget(); // Control may transfer here via 'break'. } void AstTyper::VisitWhileStatement(WhileStatement* stmt) { - RECURSE(Visit(stmt->cond())); - RECURSE(Visit(stmt->body())); - + // Collect type feedback. if (!stmt->cond()->ToBooleanIsTrue()) { stmt->cond()->RecordToBooleanTypeFeedback(oracle()); } + + store_.Forget(); // Control may transfer here via looping or 'continue'. + RECURSE(Visit(stmt->cond())); + RECURSE(Visit(stmt->body())); + store_.Forget(); // Control may transfer here via termination or 'break'. } @@ -191,45 +239,65 @@ void AstTyper::VisitForStatement(ForStatement* stmt) { if (stmt->init() != NULL) { RECURSE(Visit(stmt->init())); } + store_.Forget(); // Control may transfer here via looping. if (stmt->cond() != NULL) { - RECURSE(Visit(stmt->cond())); - + // Collect type feedback. stmt->cond()->RecordToBooleanTypeFeedback(oracle()); + + RECURSE(Visit(stmt->cond())); } RECURSE(Visit(stmt->body())); + store_.Forget(); // Control may transfer here via 'continue'. if (stmt->next() != NULL) { RECURSE(Visit(stmt->next())); } + store_.Forget(); // Control may transfer here via termination or 'break'. } void AstTyper::VisitForInStatement(ForInStatement* stmt) { + // Collect type feedback. + stmt->RecordTypeFeedback(oracle()); + RECURSE(Visit(stmt->enumerable())); + store_.Forget(); // Control may transfer here via looping or 'continue'. RECURSE(Visit(stmt->body())); - - stmt->RecordTypeFeedback(oracle()); + store_.Forget(); // Control may transfer here via 'break'. } void AstTyper::VisitForOfStatement(ForOfStatement* stmt) { RECURSE(Visit(stmt->iterable())); + store_.Forget(); // Control may transfer here via looping or 'continue'. RECURSE(Visit(stmt->body())); + store_.Forget(); // Control may transfer here via 'break'. } void AstTyper::VisitTryCatchStatement(TryCatchStatement* stmt) { + Effects try_effects = EnterEffects(); RECURSE(Visit(stmt->try_block())); + ExitEffects(); + Effects catch_effects = EnterEffects(); + store_.Forget(); // Control may transfer here via 'throw'. RECURSE(Visit(stmt->catch_block())); + ExitEffects(); + try_effects.Alt(catch_effects); + store_.Seq(try_effects); + // At this point, only variables that were reassigned in the catch block are + // still remembered. } void AstTyper::VisitTryFinallyStatement(TryFinallyStatement* stmt) { RECURSE(Visit(stmt->try_block())); + store_.Forget(); // Control may transfer here via 'throw'. RECURSE(Visit(stmt->finally_block())); } void AstTyper::VisitDebuggerStatement(DebuggerStatement* stmt) { + store_.Forget(); // May do whatever. } @@ -242,11 +310,18 @@ void AstTyper::VisitSharedFunctionInfoLiteral(SharedFunctionInfoLiteral* expr) { void AstTyper::VisitConditional(Conditional* expr) { + // Collect type feedback. + expr->condition()->RecordToBooleanTypeFeedback(oracle()); + RECURSE(Visit(expr->condition())); + Effects then_effects = EnterEffects(); RECURSE(Visit(expr->then_expression())); + ExitEffects(); + Effects else_effects = EnterEffects(); RECURSE(Visit(expr->else_expression())); - - expr->condition()->RecordToBooleanTypeFeedback(oracle()); + ExitEffects(); + then_effects.Alt(else_effects); + store_.Seq(then_effects); NarrowType(expr, Bounds::Either( expr->then_expression()->bounds(), @@ -255,7 +330,10 @@ void AstTyper::VisitConditional(Conditional* expr) { void AstTyper::VisitVariableProxy(VariableProxy* expr) { - // TODO(rossberg): typing of variables + Variable* var = expr->var(); + if (var->IsStackAllocated()) { + NarrowType(expr, store_.LookupBounds(variable_index(var))); + } } @@ -274,8 +352,8 @@ void AstTyper::VisitObjectLiteral(ObjectLiteral* expr) { ZoneList* properties = expr->properties(); for (int i = 0; i < properties->length(); ++i) { ObjectLiteral::Property* prop = properties->at(i); - RECURSE(Visit(prop->value())); + // Collect type feedback. if ((prop->kind() == ObjectLiteral::Property::MATERIALIZED_LITERAL && !CompileTimeValue::IsCompileTimeValue(prop->value())) || prop->kind() == ObjectLiteral::Property::COMPUTED) { @@ -283,6 +361,8 @@ void AstTyper::VisitObjectLiteral(ObjectLiteral* expr) { prop->RecordTypeFeedback(oracle()); } } + + RECURSE(Visit(prop->value())); } NarrowType(expr, Bounds(Type::Object(), isolate_)); @@ -303,29 +383,33 @@ void AstTyper::VisitArrayLiteral(ArrayLiteral* expr) { void AstTyper::VisitAssignment(Assignment* expr) { // TODO(rossberg): Can we clean this up? if (expr->is_compound()) { - RECURSE(Visit(expr->binary_operation())); - + // Collect type feedback. Expression* target = expr->target(); Property* prop = target->AsProperty(); if (prop != NULL) { prop->RecordTypeFeedback(oracle(), zone()); - if (!prop->key()->IsPropertyName()) { // i.e., keyed - expr->RecordTypeFeedback(oracle(), zone()); - } + expr->RecordTypeFeedback(oracle(), zone()); } + RECURSE(Visit(expr->binary_operation())); + NarrowType(expr, expr->binary_operation()->bounds()); } else { - RECURSE(Visit(expr->target())); - RECURSE(Visit(expr->value())); - - if (expr->target()->AsProperty()) { + // Collect type feedback. + if (expr->target()->IsProperty()) { expr->RecordTypeFeedback(oracle(), zone()); } + RECURSE(Visit(expr->target())); + RECURSE(Visit(expr->value())); + NarrowType(expr, expr->value()->bounds()); } - // TODO(rossberg): handle target variables + + VariableProxy* proxy = expr->target()->AsVariableProxy(); + if (proxy != NULL && proxy->var()->IsStackAllocated()) { + store_.Seq(variable_index(proxy->var()), Effect(expr->bounds())); + } } @@ -333,35 +417,31 @@ void AstTyper::VisitYield(Yield* expr) { RECURSE(Visit(expr->generator_object())); RECURSE(Visit(expr->expression())); - // We don't know anything about the type. + // We don't know anything about the result type. } void AstTyper::VisitThrow(Throw* expr) { RECURSE(Visit(expr->exception())); + // TODO(rossberg): is it worth having a non-termination effect? NarrowType(expr, Bounds(Type::None(), isolate_)); } void AstTyper::VisitProperty(Property* expr) { + // Collect type feedback. + expr->RecordTypeFeedback(oracle(), zone()); + RECURSE(Visit(expr->obj())); RECURSE(Visit(expr->key())); - expr->RecordTypeFeedback(oracle(), zone()); - - // We don't know anything about the type. + // We don't know anything about the result type. } void AstTyper::VisitCall(Call* expr) { - RECURSE(Visit(expr->expression())); - ZoneList* args = expr->arguments(); - for (int i = 0; i < args->length(); ++i) { - Expression* arg = args->at(i); - RECURSE(Visit(arg)); - } - + // Collect type feedback. Expression* callee = expr->expression(); Property* prop = callee->AsProperty(); if (prop != NULL) { @@ -371,11 +451,26 @@ void AstTyper::VisitCall(Call* expr) { expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION); } - // We don't know anything about the type. + RECURSE(Visit(expr->expression())); + ZoneList* args = expr->arguments(); + for (int i = 0; i < args->length(); ++i) { + Expression* arg = args->at(i); + RECURSE(Visit(arg)); + } + + VariableProxy* proxy = expr->expression()->AsVariableProxy(); + if (proxy != NULL && proxy->var()->is_possibly_eval(isolate())) { + store_.Forget(); // Eval could do whatever to local variables. + } + + // We don't know anything about the result type. } void AstTyper::VisitCallNew(CallNew* expr) { + // Collect type feedback. + expr->RecordTypeFeedback(oracle()); + RECURSE(Visit(expr->expression())); ZoneList* args = expr->arguments(); for (int i = 0; i < args->length(); ++i) { @@ -383,9 +478,7 @@ void AstTyper::VisitCallNew(CallNew* expr) { RECURSE(Visit(arg)); } - expr->RecordTypeFeedback(oracle()); - - // We don't know anything about the type. + // We don't know anything about the result type. } @@ -396,19 +489,19 @@ void AstTyper::VisitCallRuntime(CallRuntime* expr) { RECURSE(Visit(arg)); } - // We don't know anything about the type. + // We don't know anything about the result type. } void AstTyper::VisitUnaryOperation(UnaryOperation* expr) { - RECURSE(Visit(expr->expression())); - // Collect type feedback. if (expr->op() == Token::NOT) { // TODO(rossberg): only do in test or value context. expr->expression()->RecordToBooleanTypeFeedback(oracle()); } + RECURSE(Visit(expr->expression())); + switch (expr->op()) { case Token::NOT: case Token::DELETE: @@ -427,22 +520,25 @@ void AstTyper::VisitUnaryOperation(UnaryOperation* expr) { void AstTyper::VisitCountOperation(CountOperation* expr) { - RECURSE(Visit(expr->expression())); - + // Collect type feedback. expr->RecordTypeFeedback(oracle(), zone()); Property* prop = expr->expression()->AsProperty(); if (prop != NULL) { prop->RecordTypeFeedback(oracle(), zone()); } + RECURSE(Visit(expr->expression())); + NarrowType(expr, Bounds(Type::Smi(), Type::Number(), isolate_)); + + VariableProxy* proxy = expr->expression()->AsVariableProxy(); + if (proxy != NULL && proxy->var()->IsStackAllocated()) { + store_.Seq(variable_index(proxy->var()), Effect(expr->bounds())); + } } void AstTyper::VisitBinaryOperation(BinaryOperation* expr) { - RECURSE(Visit(expr->left())); - RECURSE(Visit(expr->right())); - // Collect type feedback. Handle type, left_type, right_type; Maybe fixed_right_arg; @@ -458,15 +554,29 @@ void AstTyper::VisitBinaryOperation(BinaryOperation* expr) { switch (expr->op()) { case Token::COMMA: + RECURSE(Visit(expr->left())); + RECURSE(Visit(expr->right())); NarrowType(expr, expr->right()->bounds()); break; case Token::OR: - case Token::AND: + case Token::AND: { + Effects left_effects = EnterEffects(); + RECURSE(Visit(expr->left())); + ExitEffects(); + Effects right_effects = EnterEffects(); + RECURSE(Visit(expr->right())); + ExitEffects(); + left_effects.Alt(right_effects); + store_.Seq(left_effects); + NarrowType(expr, Bounds::Either( expr->left()->bounds(), expr->right()->bounds(), isolate_)); break; + } case Token::BIT_OR: case Token::BIT_AND: { + RECURSE(Visit(expr->left())); + RECURSE(Visit(expr->right())); Type* upper = Type::Union( expr->left()->bounds().upper, expr->right()->bounds().upper); if (!upper->Is(Type::Signed32())) upper = Type::Signed32(); @@ -476,12 +586,18 @@ void AstTyper::VisitBinaryOperation(BinaryOperation* expr) { case Token::BIT_XOR: case Token::SHL: case Token::SAR: + RECURSE(Visit(expr->left())); + RECURSE(Visit(expr->right())); NarrowType(expr, Bounds(Type::Smi(), Type::Signed32(), isolate_)); break; case Token::SHR: + RECURSE(Visit(expr->left())); + RECURSE(Visit(expr->right())); NarrowType(expr, Bounds(Type::Smi(), Type::Unsigned32(), isolate_)); break; case Token::ADD: { + RECURSE(Visit(expr->left())); + RECURSE(Visit(expr->right())); Bounds l = expr->left()->bounds(); Bounds r = expr->right()->bounds(); Type* lower = @@ -501,6 +617,8 @@ void AstTyper::VisitBinaryOperation(BinaryOperation* expr) { case Token::MUL: case Token::DIV: case Token::MOD: + RECURSE(Visit(expr->left())); + RECURSE(Visit(expr->right())); NarrowType(expr, Bounds(Type::Smi(), Type::Number(), isolate_)); break; default: @@ -510,9 +628,6 @@ void AstTyper::VisitBinaryOperation(BinaryOperation* expr) { void AstTyper::VisitCompareOperation(CompareOperation* expr) { - RECURSE(Visit(expr->left())); - RECURSE(Visit(expr->right())); - // Collect type feedback. Handle left_type, right_type, combined_type; oracle()->CompareType(expr->CompareOperationFeedbackId(), @@ -521,6 +636,9 @@ void AstTyper::VisitCompareOperation(CompareOperation* expr) { NarrowLowerType(expr->right(), right_type); expr->set_combined_type(combined_type); + RECURSE(Visit(expr->left())); + RECURSE(Visit(expr->right())); + NarrowType(expr, Bounds(Type::Boolean(), isolate_)); } diff --git a/deps/v8/src/typing.h b/deps/v8/src/typing.h index ceef984..c942b00 100644 --- a/deps/v8/src/typing.h +++ b/deps/v8/src/typing.h @@ -35,6 +35,7 @@ #include "compiler.h" #include "type-info.h" #include "types.h" +#include "effects.h" #include "zone.h" #include "scopes.h" @@ -57,8 +58,13 @@ class AstTyper: public AstVisitor { private: explicit AstTyper(CompilationInfo* info); + static const int kNoVar = INT_MIN; + typedef v8::internal::Effects Effects; + typedef v8::internal::NestedEffects Store; + CompilationInfo* info_; TypeFeedbackOracle oracle_; + Store store_; TypeFeedbackOracle* oracle() { return &oracle_; } Zone* zone() const { return info_->zone(); } @@ -70,6 +76,17 @@ class AstTyper: public AstVisitor { e->set_bounds(Bounds::NarrowLower(e->bounds(), t, isolate_)); } + Effects EnterEffects() { + store_ = store_.Push(); + return store_.Top(); + } + void ExitEffects() { store_ = store_.Pop(); } + + int variable_index(Variable* var) { + return var->IsStackLocal() ? var->index() : + var->IsParameter() ? -var->index() : kNoVar; + } + void VisitDeclarations(ZoneList* declarations); void VisitStatements(ZoneList* statements); diff --git a/deps/v8/src/v8globals.h b/deps/v8/src/v8globals.h index c3f1f01..6ec7547 100644 --- a/deps/v8/src/v8globals.h +++ b/deps/v8/src/v8globals.h @@ -363,7 +363,8 @@ enum StateTag { GC, COMPILER, OTHER, - EXTERNAL + EXTERNAL, + IDLE }; diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index e9f1dff..5142d38 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -34,8 +34,8 @@ // system so their names cannot be changed without changing the scripts. #define MAJOR_VERSION 3 #define MINOR_VERSION 20 -#define BUILD_NUMBER 14 -#define PATCH_LEVEL 1 +#define BUILD_NUMBER 17 +#define PATCH_LEVEL 0 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) #define IS_CANDIDATE_VERSION 0 diff --git a/deps/v8/src/x64/lithium-codegen-x64.cc b/deps/v8/src/x64/lithium-codegen-x64.cc index d4c125b..abb8c77 100644 --- a/deps/v8/src/x64/lithium-codegen-x64.cc +++ b/deps/v8/src/x64/lithium-codegen-x64.cc @@ -278,6 +278,8 @@ bool LCodeGen::GenerateBody() { instr->Mnemonic()); } + RecordAndUpdatePosition(instr->position()); + instr->CompileToNative(this); } EnsureSpaceForLazyDeopt(Deoptimizer::patch_size()); @@ -331,6 +333,10 @@ bool LCodeGen::GenerateDeferredCode() { if (deferred_.length() > 0) { for (int i = 0; !is_aborted() && i < deferred_.length(); i++) { LDeferredCode* code = deferred_[i]; + + int pos = instructions_->at(code->instruction_index())->position(); + RecordAndUpdatePosition(pos); + Comment(";;; <@%d,#%d> " "-------------------- Deferred %s --------------------", code->instruction_index(), @@ -497,37 +503,57 @@ void LCodeGen::WriteTranslation(LEnvironment* environment, break; } + int object_index = 0; + int dematerialized_index = 0; for (int i = 0; i < translation_size; ++i) { LOperand* value = environment->values()->at(i); - - // TODO(mstarzinger): Introduce marker operands to indicate that this value - // is not present and must be reconstructed from the deoptimizer. Currently - // this is only used for the arguments object. - if (value == NULL) { - int arguments_count = environment->values()->length() - translation_size; - translation->BeginArgumentsObject(arguments_count); - for (int i = 0; i < arguments_count; ++i) { - LOperand* value = environment->values()->at(translation_size + i); - AddToTranslation(translation, - value, - environment->HasTaggedValueAt(translation_size + i), - environment->HasUint32ValueAt(translation_size + i)); - } - continue; - } - - AddToTranslation(translation, + AddToTranslation(environment, + translation, value, environment->HasTaggedValueAt(i), - environment->HasUint32ValueAt(i)); + environment->HasUint32ValueAt(i), + &object_index, + &dematerialized_index); } } -void LCodeGen::AddToTranslation(Translation* translation, +void LCodeGen::AddToTranslation(LEnvironment* environment, + Translation* translation, LOperand* op, bool is_tagged, - bool is_uint32) { + bool is_uint32, + int* object_index_pointer, + int* dematerialized_index_pointer) { + if (op == LEnvironment::materialization_marker()) { + int object_index = (*object_index_pointer)++; + if (environment->ObjectIsDuplicateAt(object_index)) { + int dupe_of = environment->ObjectDuplicateOfAt(object_index); + translation->DuplicateObject(dupe_of); + return; + } + int object_length = environment->ObjectLengthAt(object_index); + if (environment->ObjectIsArgumentsAt(object_index)) { + translation->BeginArgumentsObject(object_length); + } else { + translation->BeginCapturedObject(object_length); + } + int dematerialized_index = *dematerialized_index_pointer; + int env_offset = environment->translation_size() + dematerialized_index; + *dematerialized_index_pointer += object_length; + for (int i = 0; i < object_length; ++i) { + LOperand* value = environment->values()->at(env_offset + i); + AddToTranslation(environment, + translation, + value, + environment->HasTaggedValueAt(env_offset + i), + environment->HasUint32ValueAt(env_offset + i), + object_index_pointer, + dematerialized_index_pointer); + } + return; + } + if (op->IsStackSlot()) { if (is_tagged) { translation->StoreStackSlot(op->index()); @@ -667,7 +693,7 @@ void LCodeGen::DeoptimizeIf(Condition cc, ASSERT(FLAG_deopt_every_n_times == 0); // Not yet implemented on x64. - if (FLAG_trap_on_deopt && info()->IsOptimizing()) { + if (info()->ShouldTrapOnDeopt()) { Label done; if (cc != no_condition) { __ j(NegateCondition(cc), &done, Label::kNear); @@ -859,6 +885,14 @@ void LCodeGen::RecordPosition(int position) { } +void LCodeGen::RecordAndUpdatePosition(int position) { + if (position >= 0 && position != old_position_) { + masm()->positions_recorder()->RecordPosition(position); + old_position_ = position; + } +} + + static const char* LabelType(LLabel* label) { if (label->is_loop_header()) return " (loop header)"; if (label->is_osr_entry()) return " (OSR entry)"; @@ -1866,6 +1900,13 @@ void LCodeGen::EmitBranch(InstrType instr, Condition cc) { } +template +void LCodeGen::EmitFalseBranch(InstrType instr, Condition cc) { + int false_block = instr->FalseDestination(chunk_); + __ j(cc, chunk_->GetAssemblyLabel(false_block)); +} + + void LCodeGen::DoDebugBreak(LDebugBreak* instr) { __ int3(); } @@ -1996,6 +2037,12 @@ void LCodeGen::DoBranch(LBranch* instr) { __ bind(¬_string); } + if (expected.Contains(ToBooleanStub::SYMBOL)) { + // Symbol value -> true. + __ CmpInstanceType(map, SYMBOL_TYPE); + __ j(equal, instr->TrueLabel(chunk_)); + } + if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) { // heap number -> false iff +0, -0, or NaN. Label not_heap_number; @@ -2133,6 +2180,28 @@ void LCodeGen::DoCmpObjectEqAndBranch(LCmpObjectEqAndBranch* instr) { } +void LCodeGen::DoCmpHoleAndBranch(LCmpHoleAndBranch* instr) { + if (instr->hydrogen()->representation().IsTagged()) { + Register input_reg = ToRegister(instr->object()); + __ Cmp(input_reg, factory()->the_hole_value()); + EmitBranch(instr, equal); + return; + } + + XMMRegister input_reg = ToDoubleRegister(instr->object()); + __ ucomisd(input_reg, input_reg); + EmitFalseBranch(instr, parity_odd); + + __ subq(rsp, Immediate(kDoubleSize)); + __ movsd(MemOperand(rsp, 0), input_reg); + __ addq(rsp, Immediate(kDoubleSize)); + + int offset = sizeof(kHoleNanUpper32); + __ cmpl(MemOperand(rsp, -offset), Immediate(kHoleNanUpper32)); + EmitBranch(instr, equal); +} + + Condition LCodeGen::EmitIsObject(Register input, Label* is_not_object, Label* is_object) { @@ -2724,111 +2793,6 @@ void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) { } -void LCodeGen::EmitLoadFieldOrConstantFunction(Register result, - Register object, - Handle type, - Handle name, - LEnvironment* env) { - LookupResult lookup(isolate()); - type->LookupDescriptor(NULL, *name, &lookup); - ASSERT(lookup.IsFound() || lookup.IsCacheable()); - if (lookup.IsField()) { - int index = lookup.GetLocalFieldIndexFromMap(*type); - int offset = index * kPointerSize; - if (index < 0) { - // Negative property indices are in-object properties, indexed - // from the end of the fixed part of the object. - __ movq(result, FieldOperand(object, offset + type->instance_size())); - } else { - // Non-negative property indices are in the properties array. - __ movq(result, FieldOperand(object, JSObject::kPropertiesOffset)); - __ movq(result, FieldOperand(result, offset + FixedArray::kHeaderSize)); - } - } else if (lookup.IsConstant()) { - Handle constant(lookup.GetConstantFromMap(*type), isolate()); - __ LoadObject(result, constant); - } else { - // Negative lookup. - // Check prototypes. - Handle current(HeapObject::cast((*type)->prototype())); - Heap* heap = type->GetHeap(); - while (*current != heap->null_value()) { - __ LoadHeapObject(result, current); - __ Cmp(FieldOperand(result, HeapObject::kMapOffset), - Handle(current->map())); - DeoptimizeIf(not_equal, env); - current = - Handle(HeapObject::cast(current->map()->prototype())); - } - __ LoadRoot(result, Heap::kUndefinedValueRootIndex); - } -} - - -// Check for cases where EmitLoadFieldOrConstantFunction needs to walk the -// prototype chain, which causes unbounded code generation. -static bool CompactEmit(SmallMapList* list, - Handle name, - int i, - Isolate* isolate) { - Handle map = list->at(i); - LookupResult lookup(isolate); - map->LookupDescriptor(NULL, *name, &lookup); - return lookup.IsField() || lookup.IsConstant(); -} - - -void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) { - Register object = ToRegister(instr->object()); - Register result = ToRegister(instr->result()); - - int map_count = instr->hydrogen()->types()->length(); - bool need_generic = instr->hydrogen()->need_generic(); - - if (map_count == 0 && !need_generic) { - DeoptimizeIf(no_condition, instr->environment()); - return; - } - Handle name = instr->hydrogen()->name(); - Label done; - bool all_are_compact = true; - for (int i = 0; i < map_count; ++i) { - if (!CompactEmit(instr->hydrogen()->types(), name, i, isolate())) { - all_are_compact = false; - break; - } - } - for (int i = 0; i < map_count; ++i) { - bool last = (i == map_count - 1); - Handle map = instr->hydrogen()->types()->at(i); - Label check_passed; - __ CompareMap(object, map, &check_passed); - if (last && !need_generic) { - DeoptimizeIf(not_equal, instr->environment()); - __ bind(&check_passed); - EmitLoadFieldOrConstantFunction( - result, object, map, name, instr->environment()); - } else { - Label next; - bool compact = all_are_compact ? true : - CompactEmit(instr->hydrogen()->types(), name, i, isolate()); - __ j(not_equal, &next, compact ? Label::kNear : Label::kFar); - __ bind(&check_passed); - EmitLoadFieldOrConstantFunction( - result, object, map, name, instr->environment()); - __ jmp(&done, all_are_compact ? Label::kNear : Label::kFar); - __ bind(&next); - } - } - if (need_generic) { - __ Move(rcx, name); - Handle ic = isolate()->builtins()->LoadIC_Initialize(); - CallCode(ic, RelocInfo::CODE_TARGET, instr); - } - __ bind(&done); -} - - void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) { ASSERT(ToRegister(instr->object()).is(rax)); ASSERT(ToRegister(instr->result()).is(rax)); @@ -3423,7 +3387,7 @@ void LCodeGen::EmitIntegerMathAbs(LMathAbs* instr) { } -void LCodeGen::EmitInteger64MathAbs(LMathAbs* instr) { +void LCodeGen::EmitSmiMathAbs(LMathAbs* instr) { Register input_reg = ToRegister(instr->value()); __ testq(input_reg, input_reg); Label is_positive; @@ -3460,16 +3424,14 @@ void LCodeGen::DoMathAbs(LMathAbs* instr) { } else if (r.IsInteger32()) { EmitIntegerMathAbs(instr); } else if (r.IsSmi()) { - EmitInteger64MathAbs(instr); + EmitSmiMathAbs(instr); } else { // Tagged case. DeferredMathAbsTaggedHeapNumber* deferred = new(zone()) DeferredMathAbsTaggedHeapNumber(this, instr); Register input_reg = ToRegister(instr->value()); // Smi check. __ JumpIfNotSmi(input_reg, deferred->entry()); - __ SmiToInteger32(input_reg, input_reg); - EmitIntegerMathAbs(instr); - __ Integer32ToSmi(input_reg, input_reg); + EmitSmiMathAbs(instr); __ bind(deferred->exit()); } } @@ -4583,36 +4545,6 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { Register reg = ToRegister(instr->result()); Register tmp = ToRegister(instr->temp()); - bool convert_hole = false; - HValue* change_input = instr->hydrogen()->value(); - if (change_input->IsLoadKeyed()) { - HLoadKeyed* load = HLoadKeyed::cast(change_input); - convert_hole = load->UsesMustHandleHole(); - } - - Label no_special_nan_handling; - Label done; - if (convert_hole) { - XMMRegister input_reg = ToDoubleRegister(instr->value()); - __ ucomisd(input_reg, input_reg); - __ j(parity_odd, &no_special_nan_handling); - __ subq(rsp, Immediate(kDoubleSize)); - __ movsd(MemOperand(rsp, 0), input_reg); - __ cmpl(MemOperand(rsp, sizeof(kHoleNanLower32)), - Immediate(kHoleNanUpper32)); - Label canonicalize; - __ j(not_equal, &canonicalize); - __ addq(rsp, Immediate(kDoubleSize)); - __ Move(reg, factory()->the_hole_value()); - __ jmp(&done); - __ bind(&canonicalize); - __ addq(rsp, Immediate(kDoubleSize)); - __ Set(kScratchRegister, BitCast( - FixedDoubleArray::canonical_not_the_hole_nan_as_double())); - __ movq(input_reg, kScratchRegister); - } - - __ bind(&no_special_nan_handling); DeferredNumberTagD* deferred = new(zone()) DeferredNumberTagD(this, instr); if (FLAG_inline_new) { __ AllocateHeapNumber(reg, tmp, deferred->entry()); @@ -4621,8 +4553,6 @@ void LCodeGen::DoNumberTagD(LNumberTagD* instr) { } __ bind(deferred->exit()); __ movsd(FieldOperand(reg, HeapNumber::kValueOffset), input_reg); - - __ bind(&done); } @@ -4666,22 +4596,20 @@ void LCodeGen::DoSmiUntag(LSmiUntag* instr) { void LCodeGen::EmitNumberUntagD(Register input_reg, XMMRegister result_reg, - bool allow_undefined_as_nan, + bool can_convert_undefined_to_nan, bool deoptimize_on_minus_zero, LEnvironment* env, NumberUntagDMode mode) { Label load_smi, done; - STATIC_ASSERT(NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE > - NUMBER_CANDIDATE_IS_ANY_TAGGED); - if (mode >= NUMBER_CANDIDATE_IS_ANY_TAGGED) { + if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED) { // Smi check. __ JumpIfSmi(input_reg, &load_smi, Label::kNear); // Heap number map check. __ CompareRoot(FieldOperand(input_reg, HeapObject::kMapOffset), Heap::kHeapNumberMapRootIndex); - if (!allow_undefined_as_nan) { + if (!can_convert_undefined_to_nan) { DeoptimizeIf(not_equal, env); } else { Label heap_number, convert; @@ -4689,10 +4617,6 @@ void LCodeGen::EmitNumberUntagD(Register input_reg, // Convert undefined (and hole) to NaN. Compute NaN as 0/0. __ CompareRoot(input_reg, Heap::kUndefinedValueRootIndex); - if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE) { - __ j(equal, &convert, Label::kNear); - __ CompareRoot(input_reg, Heap::kTheHoleValueRootIndex); - } DeoptimizeIf(not_equal, env); __ bind(&convert); @@ -4805,19 +4729,12 @@ void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) { Register input_reg = ToRegister(input); XMMRegister result_reg = ToDoubleRegister(result); - NumberUntagDMode mode = NUMBER_CANDIDATE_IS_ANY_TAGGED; HValue* value = instr->hydrogen()->value(); - if (value->type().IsSmi()) { - mode = NUMBER_CANDIDATE_IS_SMI; - } else if (value->IsLoadKeyed()) { - HLoadKeyed* load = HLoadKeyed::cast(value); - if (load->UsesMustHandleHole()) { - mode = NUMBER_CANDIDATE_IS_ANY_TAGGED_CONVERT_HOLE; - } - } + NumberUntagDMode mode = value->representation().IsSmi() + ? NUMBER_CANDIDATE_IS_SMI : NUMBER_CANDIDATE_IS_ANY_TAGGED; EmitNumberUntagD(input_reg, result_reg, - instr->hydrogen()->allow_undefined_as_nan(), + instr->hydrogen()->can_convert_undefined_to_nan(), instr->hydrogen()->deoptimize_on_minus_zero(), instr->environment(), mode); @@ -5421,6 +5338,8 @@ void LCodeGen::DoDeoptimize(LDeoptimize* instr) { if (info()->IsStub() && type == Deoptimizer::EAGER) { type = Deoptimizer::LAZY; } + + Comment(";;; deoptimize: %s", instr->hydrogen()->reason()); DeoptimizeIf(no_condition, instr->environment(), type); } diff --git a/deps/v8/src/x64/lithium-codegen-x64.h b/deps/v8/src/x64/lithium-codegen-x64.h index e134229..a74ec79 100644 --- a/deps/v8/src/x64/lithium-codegen-x64.h +++ b/deps/v8/src/x64/lithium-codegen-x64.h @@ -67,7 +67,8 @@ class LCodeGen BASE_EMBEDDED { frame_is_built_(false), safepoints_(info->zone()), resolver_(this), - expected_safepoint_kind_(Safepoint::kSimple) { + expected_safepoint_kind_(Safepoint::kSimple), + old_position_(RelocInfo::kNoPosition) { PopulateDeoptimizationLiteralsWithInlinedFunctions(); } @@ -235,7 +236,6 @@ class LCodeGen BASE_EMBEDDED { CallKind call_kind, RDIState rdi_state); - void RecordSafepointWithLazyDeopt(LInstruction* instr, SafepointMode safepoint_mode, int argc); @@ -246,10 +246,14 @@ class LCodeGen BASE_EMBEDDED { Deoptimizer::BailoutType bailout_type); void DeoptimizeIf(Condition cc, LEnvironment* environment); void ApplyCheckIf(Condition cc, LBoundsCheck* check); - void AddToTranslation(Translation* translation, + + void AddToTranslation(LEnvironment* environment, + Translation* translation, LOperand* op, bool is_tagged, - bool is_uint32); + bool is_uint32, + int* object_index_pointer, + int* dematerialized_index_pointer); void RegisterDependentCodeForEmbeddedMaps(Handle code); void PopulateDeoptimizationData(Handle code); int DefineDeoptimizationLiteral(Handle literal); @@ -266,7 +270,7 @@ class LCodeGen BASE_EMBEDDED { uint32_t additional_index = 0); void EmitIntegerMathAbs(LMathAbs* instr); - void EmitInteger64MathAbs(LMathAbs* instr); + void EmitSmiMathAbs(LMathAbs* instr); // Support for recording safepoint and position information. void RecordSafepoint(LPointerMap* pointers, @@ -279,11 +283,14 @@ class LCodeGen BASE_EMBEDDED { int arguments, Safepoint::DeoptMode mode); void RecordPosition(int position); + void RecordAndUpdatePosition(int position); static Condition TokenToCondition(Token::Value op, bool is_unsigned); void EmitGoto(int block); template void EmitBranch(InstrType instr, Condition cc); + template + void EmitFalseBranch(InstrType instr, Condition cc); void EmitNumberUntagD( Register input, XMMRegister result, @@ -319,12 +326,6 @@ class LCodeGen BASE_EMBEDDED { // Caller should branch on equal condition. void EmitIsConstructCall(Register temp); - void EmitLoadFieldOrConstantFunction(Register result, - Register object, - Handle type, - Handle name, - LEnvironment* env); - // Emits code for pushing either a tagged constant, a (non-double) // register, or a stack slot operand. void EmitPushTaggedOperand(LOperand* operand); @@ -381,6 +382,8 @@ class LCodeGen BASE_EMBEDDED { Safepoint::Kind expected_safepoint_kind_; + int old_position_; + class PushSafepointRegistersScope BASE_EMBEDDED { public: explicit PushSafepointRegistersScope(LCodeGen* codegen) diff --git a/deps/v8/src/x64/lithium-x64.cc b/deps/v8/src/x64/lithium-x64.cc index 913e170..c058b0d 100644 --- a/deps/v8/src/x64/lithium-x64.cc +++ b/deps/v8/src/x64/lithium-x64.cc @@ -601,8 +601,10 @@ LInstruction* LChunkBuilder::DefineFixedDouble( LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { HEnvironment* hydrogen_env = current_block_->last_environment(); int argument_index_accumulator = 0; + ZoneList objects_to_materialize(0, zone()); instr->set_environment(CreateEnvironment(hydrogen_env, - &argument_index_accumulator)); + &argument_index_accumulator, + &objects_to_materialize)); return instr; } @@ -812,7 +814,7 @@ void LChunkBuilder::DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block) { HEnvironment* last_environment = pred->last_environment(); for (int i = 0; i < block->phis()->length(); ++i) { HPhi* phi = block->phis()->at(i); - if (phi->merged_index() < last_environment->length()) { + if (phi->HasMergedIndex()) { last_environment->SetValueAt(phi->merged_index(), phi); } } @@ -882,6 +884,7 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { } #endif + instr->set_position(position_); if (FLAG_stress_pointer_maps && !instr->HasPointerMap()) { instr = AssignPointerMap(instr); } @@ -897,11 +900,13 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { LEnvironment* LChunkBuilder::CreateEnvironment( HEnvironment* hydrogen_env, - int* argument_index_accumulator) { + int* argument_index_accumulator, + ZoneList* objects_to_materialize) { if (hydrogen_env == NULL) return NULL; - LEnvironment* outer = - CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); + LEnvironment* outer = CreateEnvironment(hydrogen_env->outer(), + argument_index_accumulator, + objects_to_materialize); BailoutId ast_id = hydrogen_env->ast_id(); ASSERT(!ast_id.IsNone() || hydrogen_env->frame_type() != JS_FUNCTION); @@ -916,16 +921,16 @@ LEnvironment* LChunkBuilder::CreateEnvironment( outer, hydrogen_env->entry(), zone()); - bool needs_arguments_object_materialization = false; int argument_index = *argument_index_accumulator; + int object_index = objects_to_materialize->length(); for (int i = 0; i < hydrogen_env->length(); ++i) { if (hydrogen_env->is_special_index(i)) continue; + LOperand* op; HValue* value = hydrogen_env->values()->at(i); - LOperand* op = NULL; - if (value->IsArgumentsObject()) { - needs_arguments_object_materialization = true; - op = NULL; + if (value->IsArgumentsObject() || value->IsCapturedObject()) { + objects_to_materialize->Add(value, zone()); + op = LEnvironment::materialization_marker(); } else if (value->IsPushArgument()) { op = new(zone()) LArgument(argument_index++); } else { @@ -936,15 +941,33 @@ LEnvironment* LChunkBuilder::CreateEnvironment( value->CheckFlag(HInstruction::kUint32)); } - if (needs_arguments_object_materialization) { - HArgumentsObject* arguments = hydrogen_env->entry() == NULL - ? graph()->GetArgumentsObject() - : hydrogen_env->entry()->arguments_object(); - ASSERT(arguments->IsLinked()); - for (int i = 1; i < arguments->arguments_count(); ++i) { - HValue* value = arguments->arguments_values()->at(i); - ASSERT(!value->IsArgumentsObject() && !value->IsPushArgument()); - LOperand* op = UseAny(value); + for (int i = object_index; i < objects_to_materialize->length(); ++i) { + HValue* object_to_materialize = objects_to_materialize->at(i); + int previously_materialized_object = -1; + for (int prev = 0; prev < i; ++prev) { + if (objects_to_materialize->at(prev) == objects_to_materialize->at(i)) { + previously_materialized_object = prev; + break; + } + } + int length = object_to_materialize->OperandCount(); + bool is_arguments = object_to_materialize->IsArgumentsObject(); + if (previously_materialized_object >= 0) { + result->AddDuplicateObject(previously_materialized_object); + continue; + } else { + result->AddNewObject(is_arguments ? length - 1 : length, is_arguments); + } + for (int i = is_arguments ? 1 : 0; i < length; ++i) { + LOperand* op; + HValue* value = object_to_materialize->OperandAt(i); + if (value->IsArgumentsObject() || value->IsCapturedObject()) { + objects_to_materialize->Add(value, zone()); + op = LEnvironment::materialization_marker(); + } else { + ASSERT(!value->IsPushArgument()); + op = UseAny(value); + } result->AddValue(op, value->representation(), value->CheckFlag(HInstruction::kUint32)); @@ -1589,9 +1612,8 @@ LInstruction* LChunkBuilder::DoCompareNumericAndBranch( HCompareNumericAndBranch* instr) { Representation r = instr->representation(); if (r.IsSmiOrInteger32()) { - ASSERT(instr->left()->representation().IsSmiOrInteger32()); - ASSERT(instr->left()->representation().Equals( - instr->right()->representation())); + ASSERT(instr->left()->representation().Equals(r)); + ASSERT(instr->right()->representation().Equals(r)); LOperand* left = UseRegisterOrConstantAtStart(instr->left()); LOperand* right = UseOrConstantAtStart(instr->right()); return new(zone()) LCompareNumericAndBranch(left, right); @@ -1621,6 +1643,13 @@ LInstruction* LChunkBuilder::DoCompareObjectEqAndBranch( } +LInstruction* LChunkBuilder::DoCompareHoleAndBranch( + HCompareHoleAndBranch* instr) { + LOperand* object = UseRegisterAtStart(instr->object()); + return new(zone()) LCmpHoleAndBranch(object); +} + + LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) { ASSERT(instr->value()->representation().IsTagged()); return new(zone()) LIsObjectAndBranch(UseRegisterAtStart(instr->value())); @@ -2036,23 +2065,6 @@ LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) { } -LInstruction* LChunkBuilder::DoLoadNamedFieldPolymorphic( - HLoadNamedFieldPolymorphic* instr) { - ASSERT(instr->representation().IsTagged()); - if (instr->need_generic()) { - LOperand* obj = UseFixed(instr->object(), rax); - LLoadNamedFieldPolymorphic* result = - new(zone()) LLoadNamedFieldPolymorphic(obj); - return MarkAsCall(DefineFixed(result, rax), instr); - } else { - LOperand* obj = UseRegisterAtStart(instr->object()); - LLoadNamedFieldPolymorphic* result = - new(zone()) LLoadNamedFieldPolymorphic(obj); - return AssignEnvironment(DefineAsRegister(result)); - } -} - - LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) { LOperand* object = UseFixed(instr->object(), rax); LLoadNamedGeneric* result = new(zone()) LLoadNamedGeneric(object); @@ -2366,6 +2378,12 @@ LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) { } +LInstruction* LChunkBuilder::DoCapturedObject(HCapturedObject* instr) { + // There are no real uses of a captured object. + return NULL; +} + + LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) { info()->MarkAsRequiresFrame(); LOperand* args = UseRegister(instr->arguments()); diff --git a/deps/v8/src/x64/lithium-x64.h b/deps/v8/src/x64/lithium-x64.h index c3b9db4..77bebe6 100644 --- a/deps/v8/src/x64/lithium-x64.h +++ b/deps/v8/src/x64/lithium-x64.h @@ -74,6 +74,7 @@ class LCodeGen; V(ClassOfTestAndBranch) \ V(CompareNumericAndBranch) \ V(CmpObjectEqAndBranch) \ + V(CmpHoleAndBranch) \ V(CmpMapAndBranch) \ V(CmpT) \ V(ConstantD) \ @@ -126,7 +127,6 @@ class LCodeGen; V(LoadKeyed) \ V(LoadKeyedGeneric) \ V(LoadNamedField) \ - V(LoadNamedFieldPolymorphic) \ V(LoadNamedGeneric) \ V(MapEnumLength) \ V(MathAbs) \ @@ -205,9 +205,11 @@ class LCodeGen; class LInstruction: public ZoneObject { public: LInstruction() - : environment_(NULL), - hydrogen_value_(NULL), - is_call_(false) { } + : environment_(NULL), + hydrogen_value_(NULL), + bit_field_(IsCallBits::encode(false)) { + set_position(RelocInfo::kNoPosition); + } virtual ~LInstruction() { } @@ -247,20 +249,30 @@ class LInstruction: public ZoneObject { LPointerMap* pointer_map() const { return pointer_map_.get(); } bool HasPointerMap() const { return pointer_map_.is_set(); } + // The 31 bits PositionBits is used to store the int position value. And the + // position value may be RelocInfo::kNoPosition (-1). The accessor always + // +1/-1 so that the encoded value of position in bit_field_ is always >= 0 + // and can fit into the 31 bits PositionBits. + void set_position(int pos) { + bit_field_ = PositionBits::update(bit_field_, pos + 1); + } + int position() { return PositionBits::decode(bit_field_) - 1; } + void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; } HValue* hydrogen_value() const { return hydrogen_value_; } - void MarkAsCall() { is_call_ = true; } + void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); } + bool IsCall() const { return IsCallBits::decode(bit_field_); } // Interface to the register allocator and iterators. - bool ClobbersTemps() const { return is_call_; } - bool ClobbersRegisters() const { return is_call_; } - bool ClobbersDoubleRegisters() const { return is_call_; } + bool ClobbersTemps() const { return IsCall(); } + bool ClobbersRegisters() const { return IsCall(); } + bool ClobbersDoubleRegisters() const { return IsCall(); } virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { } // Interface to the register allocator and iterators. - bool IsMarkedAsCall() const { return is_call_; } + bool IsMarkedAsCall() const { return IsCall(); } virtual bool HasResult() const = 0; virtual LOperand* result() const = 0; @@ -284,10 +296,13 @@ class LInstruction: public ZoneObject { virtual int TempCount() = 0; virtual LOperand* TempAt(int i) = 0; + class IsCallBits: public BitField {}; + class PositionBits: public BitField {}; + LEnvironment* environment_; SetOncePointer pointer_map_; HValue* hydrogen_value_; - bool is_call_; + int bit_field_; }; @@ -823,8 +838,20 @@ class LCmpObjectEqAndBranch: public LControlInstruction<2, 0> { LOperand* left() { return inputs_[0]; } LOperand* right() { return inputs_[1]; } - DECLARE_CONCRETE_INSTRUCTION(CmpObjectEqAndBranch, - "cmp-object-eq-and-branch") + DECLARE_CONCRETE_INSTRUCTION(CmpObjectEqAndBranch, "cmp-object-eq-and-branch") +}; + + +class LCmpHoleAndBranch: public LControlInstruction<1, 0> { + public: + explicit LCmpHoleAndBranch(LOperand* object) { + inputs_[0] = object; + } + + LOperand* object() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(CmpHoleAndBranch, "cmp-hole-and-branch") + DECLARE_HYDROGEN_ACCESSOR(CompareHoleAndBranch) }; @@ -1451,19 +1478,6 @@ class LLoadNamedField: public LTemplateInstruction<1, 1, 0> { }; -class LLoadNamedFieldPolymorphic: public LTemplateInstruction<1, 1, 0> { - public: - explicit LLoadNamedFieldPolymorphic(LOperand* object) { - inputs_[0] = object; - } - - DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field-polymorphic") - DECLARE_HYDROGEN_ACCESSOR(LoadNamedFieldPolymorphic) - - LOperand* object() { return inputs_[0]; } -}; - - class LLoadNamedGeneric: public LTemplateInstruction<1, 1, 0> { public: explicit LLoadNamedGeneric(LOperand* object) { @@ -2627,7 +2641,8 @@ class LChunkBuilder BASE_EMBEDDED { CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY); LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env, - int* argument_index_accumulator); + int* argument_index_accumulator, + ZoneList* objects_to_materialize); void VisitInstruction(HInstruction* current); diff --git a/deps/v8/src/x64/stub-cache-x64.cc b/deps/v8/src/x64/stub-cache-x64.cc index 7ad250a..34a557b 100644 --- a/deps/v8/src/x64/stub-cache-x64.cc +++ b/deps/v8/src/x64/stub-cache-x64.cc @@ -2246,26 +2246,25 @@ Handle CallStubCompiler::CompileMathAbsCall( Label not_smi; STATIC_ASSERT(kSmiTag == 0); __ JumpIfNotSmi(rax, ¬_smi); - __ SmiToInteger32(rax, rax); + // Branchless abs implementation, refer to below: + // http://graphics.stanford.edu/~seander/bithacks.html#IntegerAbs // Set ebx to 1...1 (== -1) if the argument is negative, or to 0...0 // otherwise. - __ movl(rbx, rax); - __ sarl(rbx, Immediate(kBitsPerInt - 1)); + __ movq(rbx, rax); + __ sar(rbx, Immediate(kBitsPerPointer - 1)); // Do bitwise not or do nothing depending on ebx. - __ xorl(rax, rbx); + __ xor_(rax, rbx); // Add 1 or do nothing depending on ebx. - __ subl(rax, rbx); + __ subq(rax, rbx); // If the result is still negative, go to the slow case. // This only happens for the most negative smi. Label slow; __ j(negative, &slow); - // Smi case done. - __ Integer32ToSmi(rax, rax); __ ret(2 * kPointerSize); // Check if the argument is a heap number and load its value. diff --git a/deps/v8/src/zone-inl.h b/deps/v8/src/zone-inl.h index 49e7626..f257382 100644 --- a/deps/v8/src/zone-inl.h +++ b/deps/v8/src/zone-inl.h @@ -109,6 +109,12 @@ void* ZoneList::operator new(size_t size, Zone* zone) { } +template +void* ZoneSplayTree::operator new(size_t size, Zone* zone) { + return zone->New(static_cast(size)); +} + + } } // namespace v8::internal #endif // V8_ZONE_INL_H_ diff --git a/deps/v8/src/zone.h b/deps/v8/src/zone.h index 1f14115..bd7cc39 100644 --- a/deps/v8/src/zone.h +++ b/deps/v8/src/zone.h @@ -177,6 +177,7 @@ struct ZoneAllocationPolicy { explicit ZoneAllocationPolicy(Zone* zone) : zone_(zone) { } INLINE(void* New(size_t size)); INLINE(static void Delete(void *pointer)) { } + Zone* zone() { return zone_; } private: Zone* zone_; @@ -201,7 +202,7 @@ class ZoneList: public List { ZoneList(const ZoneList& other, Zone* zone) : List(other.length(), ZoneAllocationPolicy(zone)) { - AddAll(other, ZoneAllocationPolicy(zone)); + AddAll(other, zone); } // We add some convenience wrappers so that we can pass in a Zone @@ -209,8 +210,7 @@ class ZoneList: public List { INLINE(void Add(const T& element, Zone* zone)) { List::Add(element, ZoneAllocationPolicy(zone)); } - INLINE(void AddAll(const List& other, - Zone* zone)) { + INLINE(void AddAll(const List& other, Zone* zone)) { List::AddAll(other, ZoneAllocationPolicy(zone)); } INLINE(void AddAll(const Vector& other, Zone* zone)) { diff --git a/deps/v8/test/cctest/cctest.h b/deps/v8/test/cctest/cctest.h index 1282d7d..193126a 100644 --- a/deps/v8/test/cctest/cctest.h +++ b/deps/v8/test/cctest/cctest.h @@ -300,57 +300,4 @@ static inline void SimulateFullSpace(v8::internal::PagedSpace* space) { } -// Adapted from http://en.wikipedia.org/wiki/Multiply-with-carry -class RandomNumberGenerator { - public: - RandomNumberGenerator() { - init(); - } - - void init(uint32_t seed = 0x5688c73e) { - static const uint32_t phi = 0x9e3779b9; - c = 362436; - i = kQSize-1; - Q[0] = seed; - Q[1] = seed + phi; - Q[2] = seed + phi + phi; - for (unsigned j = 3; j < kQSize; j++) { - Q[j] = Q[j - 3] ^ Q[j - 2] ^ phi ^ j; - } - } - - uint32_t next() { - uint64_t a = 18782; - uint32_t r = 0xfffffffe; - i = (i + 1) & (kQSize-1); - uint64_t t = a * Q[i] + c; - c = (t >> 32); - uint32_t x = static_cast(t + c); - if (x < c) { - x++; - c++; - } - return (Q[i] = r - x); - } - - uint32_t next(int max) { - return next() % max; - } - - bool next(double threshold) { - ASSERT(threshold >= 0.0 && threshold <= 1.0); - if (threshold == 1.0) return true; - if (threshold == 0.0) return false; - uint32_t value = next() % 100000; - return threshold > static_cast(value)/100000.0; - } - - private: - static const uint32_t kQSize = 4096; - uint32_t Q[kQSize]; - uint32_t c; - uint32_t i; -}; - - #endif // ifndef CCTEST_H_ diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index 664f905..3be5008 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -19915,16 +19915,26 @@ THREADED_TEST(Regress260106) { } -THREADED_TEST(JSONParse) { +THREADED_TEST(JSONParseObject) { LocalContext context; HandleScope scope(context->GetIsolate()); - Local obj = v8::JSON::Parse(v8_str("{\"x\":42}")); + Local obj = v8::JSON::Parse(v8_str("{\"x\":42}")); Handle global = context->Global(); global->Set(v8_str("obj"), obj); ExpectString("JSON.stringify(obj)", "{\"x\":42}"); } +THREADED_TEST(JSONParseNumber) { + LocalContext context; + HandleScope scope(context->GetIsolate()); + Local obj = v8::JSON::Parse(v8_str("42")); + Handle global = context->Global(); + global->Set(v8_str("obj"), obj); + ExpectString("JSON.stringify(obj)", "42"); +} + + #ifndef WIN32 class ThreadInterruptTest { public: diff --git a/deps/v8/test/cctest/test-cpu-profiler.cc b/deps/v8/test/cctest/test-cpu-profiler.cc index daf8db6..7b5fc2b 100644 --- a/deps/v8/test/cctest/test-cpu-profiler.cc +++ b/deps/v8/test/cctest/test-cpu-profiler.cc @@ -1324,3 +1324,58 @@ TEST(JsNative1JsNative2JsSample) { v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); cpu_profiler->DeleteAllCpuProfiles(); } + + +// [Top down]: +// 6 0 (root) #0 1 +// 3 3 (program) #0 2 +// 3 3 (idle) #0 3 +TEST(IdleTime) { + LocalContext env; + v8::HandleScope scope(env->GetIsolate()); + v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); + + v8::Local profile_name = v8::String::New("my_profile"); + cpu_profiler->StartCpuProfiling(profile_name); + + i::Isolate* isolate = i::Isolate::Current(); + i::ProfilerEventsProcessor* processor = isolate->cpu_profiler()->processor(); + processor->AddCurrentStack(isolate); + + cpu_profiler->SetIdle(true); + + for (int i = 0; i < 3; i++) { + processor->AddCurrentStack(isolate); + } + + cpu_profiler->SetIdle(false); + processor->AddCurrentStack(isolate); + + + const v8::CpuProfile* profile = cpu_profiler->StopCpuProfiling(profile_name); + CHECK_NE(NULL, profile); + // Dump collected profile to have a better diagnostic in case of failure. + reinterpret_cast( + const_cast(profile))->Print(); + + const v8::CpuProfileNode* root = profile->GetTopDownRoot(); + ScopedVector > names(3); + names[0] = v8::String::New(ProfileGenerator::kGarbageCollectorEntryName); + names[1] = v8::String::New(ProfileGenerator::kProgramEntryName); + names[2] = v8::String::New(ProfileGenerator::kIdleEntryName); + CheckChildrenNames(root, names); + + const v8::CpuProfileNode* programNode = + GetChild(root, ProfileGenerator::kProgramEntryName); + CHECK_EQ(0, programNode->GetChildrenCount()); + CHECK_GE(programNode->GetSelfSamplesCount(), 3); + CHECK_GE(programNode->GetHitCount(), 3); + + const v8::CpuProfileNode* idleNode = + GetChild(root, ProfileGenerator::kIdleEntryName); + CHECK_EQ(0, idleNode->GetChildrenCount()); + CHECK_GE(idleNode->GetSelfSamplesCount(), 3); + CHECK_GE(idleNode->GetHitCount(), 3); + + cpu_profiler->DeleteAllCpuProfiles(); +} diff --git a/deps/v8/test/cctest/test-deoptimization.cc b/deps/v8/test/cctest/test-deoptimization.cc index dfc2754..c164193 100644 --- a/deps/v8/test/cctest/test-deoptimization.cc +++ b/deps/v8/test/cctest/test-deoptimization.cc @@ -77,23 +77,27 @@ class AlwaysOptimizeAllowNativesSyntaxNoInlining { // Utility class to set --allow-natives-syntax and --nouse-inlining when // constructed and return to their default state when destroyed. -class AllowNativesSyntaxNoInlining { +class AllowNativesSyntaxNoInliningNoParallel { public: - AllowNativesSyntaxNoInlining() + AllowNativesSyntaxNoInliningNoParallel() : allow_natives_syntax_(i::FLAG_allow_natives_syntax), - use_inlining_(i::FLAG_use_inlining) { + use_inlining_(i::FLAG_use_inlining), + parallel_recompilation_(i::FLAG_parallel_recompilation) { i::FLAG_allow_natives_syntax = true; i::FLAG_use_inlining = false; + i::FLAG_parallel_recompilation = false; } - ~AllowNativesSyntaxNoInlining() { + ~AllowNativesSyntaxNoInliningNoParallel() { i::FLAG_allow_natives_syntax = allow_natives_syntax_; i::FLAG_use_inlining = use_inlining_; + i::FLAG_parallel_recompilation = parallel_recompilation_; } private: bool allow_natives_syntax_; bool use_inlining_; + bool parallel_recompilation_; }; @@ -343,7 +347,7 @@ TEST(DeoptimizeBinaryOperationADDString) { const char* f_source = "function f(x, y) { return x + y; };"; { - AllowNativesSyntaxNoInlining options; + AllowNativesSyntaxNoInliningNoParallel options; // Compile function f and collect to type feedback to insert binary op stub // call in the optimized code. i::FLAG_prepare_always_opt = true; @@ -401,7 +405,7 @@ static void TestDeoptimizeBinaryOpHelper(LocalContext* env, binary_op); char* f_source = f_source_buffer.start(); - AllowNativesSyntaxNoInlining options; + AllowNativesSyntaxNoInliningNoParallel options; // Compile function f and collect to type feedback to insert binary op stub // call in the optimized code. i::FLAG_prepare_always_opt = true; @@ -493,7 +497,7 @@ TEST(DeoptimizeCompare) { const char* f_source = "function f(x, y) { return x < y; };"; { - AllowNativesSyntaxNoInlining options; + AllowNativesSyntaxNoInliningNoParallel options; // Compile function f and collect to type feedback to insert compare ic // call in the optimized code. i::FLAG_prepare_always_opt = true; @@ -540,7 +544,7 @@ TEST(DeoptimizeLoadICStoreIC) { const char* g2_source = "function g2(x, y) { x[y] = 1; };"; { - AllowNativesSyntaxNoInlining options; + AllowNativesSyntaxNoInliningNoParallel options; // Compile functions and collect to type feedback to insert ic // calls in the optimized code. i::FLAG_prepare_always_opt = true; @@ -620,7 +624,7 @@ TEST(DeoptimizeLoadICStoreICNested) { const char* g2_source = "function g2(x, y) { x[y] = 1; };"; { - AllowNativesSyntaxNoInlining options; + AllowNativesSyntaxNoInliningNoParallel options; // Compile functions and collect to type feedback to insert ic // calls in the optimized code. i::FLAG_prepare_always_opt = true; diff --git a/deps/v8/test/cctest/test-global-handles.cc b/deps/v8/test/cctest/test-global-handles.cc index ea11dbc..0b652db 100644 --- a/deps/v8/test/cctest/test-global-handles.cc +++ b/deps/v8/test/cctest/test-global-handles.cc @@ -25,9 +25,6 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include -#include - #include "global-handles.h" #include "cctest.h" @@ -320,157 +317,6 @@ TEST(ImplicitReferences) { } -static const int kBlockSize = 256; - - -TEST(BlockCollection) { - v8::V8::Initialize(); - Isolate* isolate = Isolate::Current(); - GlobalHandles* global_handles = isolate->global_handles(); - CHECK_EQ(0, global_handles->block_count()); - CHECK_EQ(0, global_handles->global_handles_count()); - Object* object = isolate->heap()->undefined_value(); - const int kNumberOfBlocks = 5; - typedef Handle Block[kBlockSize]; - for (int round = 0; round < 3; round++) { - Block blocks[kNumberOfBlocks]; - for (int i = 0; i < kNumberOfBlocks; i++) { - for (int j = 0; j < kBlockSize; j++) { - blocks[i][j] = global_handles->Create(object); - } - } - CHECK_EQ(kNumberOfBlocks, global_handles->block_count()); - for (int i = 0; i < kNumberOfBlocks; i++) { - for (int j = 0; j < kBlockSize; j++) { - global_handles->Destroy(blocks[i][j].location()); - } - } - isolate->heap()->CollectAllAvailableGarbage("BlockCollection"); - CHECK_EQ(0, global_handles->global_handles_count()); - CHECK_EQ(1, global_handles->block_count()); - } -} - - -class RandomMutationData { - public: - explicit RandomMutationData(Isolate* isolate) - : isolate_(isolate), weak_offset_(0) {} - - void Mutate(double strong_growth_tendency, - double weak_growth_tendency = 0.05) { - for (int i = 0; i < kBlockSize * 100; i++) { - if (rng_.next(strong_growth_tendency)) { - AddStrong(); - } else if (strong_nodes_.size() != 0) { - size_t to_remove = rng_.next(static_cast(strong_nodes_.size())); - RemoveStrong(to_remove); - } - if (rng_.next(weak_growth_tendency)) AddWeak(); - if (rng_.next(0.05)) { -#ifdef DEBUG - isolate_->global_handles()->VerifyBlockInvariants(); -#endif - } - if (rng_.next(0.0001)) { - isolate_->heap()->PerformScavenge(); - } else if (rng_.next(0.00003)) { - isolate_->heap()->CollectAllAvailableGarbage(); - } - CheckSizes(); - } - } - - void RemoveAll() { - while (strong_nodes_.size() != 0) { - RemoveStrong(strong_nodes_.size() - 1); - } - isolate_->heap()->PerformScavenge(); - isolate_->heap()->CollectAllAvailableGarbage(); - CheckSizes(); - } - - private: - typedef std::vector NodeVector; - typedef std::map NodeMap; - - void CheckSizes() { - int stored_sizes = - static_cast(strong_nodes_.size() + weak_nodes_.size()); - CHECK_EQ(isolate_->global_handles()->global_handles_count(), stored_sizes); - } - - void AddStrong() { - Object* object = isolate_->heap()->undefined_value(); - Object** location = isolate_->global_handles()->Create(object).location(); - strong_nodes_.push_back(location); - } - - void RemoveStrong(size_t offset) { - isolate_->global_handles()->Destroy(strong_nodes_.at(offset)); - strong_nodes_.erase(strong_nodes_.begin() + offset); - } - - void AddWeak() { - v8::Isolate* isolate = reinterpret_cast(isolate_); - v8::HandleScope scope(isolate); - v8::Local object = v8::Object::New(); - int32_t offset = ++weak_offset_; - object->Set(7, v8::Integer::New(offset, isolate)); - v8::Persistent persistent(isolate, object); - persistent.MakeWeak(isolate, this, WeakCallback); - persistent.MarkIndependent(); - Object** location = v8::Utils::OpenPersistent(persistent).location(); - bool inserted = - weak_nodes_.insert(std::make_pair(offset, location)).second; - CHECK(inserted); - } - - static void WeakCallback(v8::Isolate* isolate, - v8::Persistent* persistent, - RandomMutationData* data) { - v8::Local object = - v8::Local::New(isolate, *persistent); - int32_t offset = - v8::Local::Cast(object->Get(7))->Int32Value(); - Object** location = v8::Utils::OpenPersistent(persistent).location(); - NodeMap& weak_nodes = data->weak_nodes_; - NodeMap::iterator it = weak_nodes.find(offset); - CHECK(it != weak_nodes.end()); - CHECK(it->second == location); - weak_nodes.erase(it); - persistent->Dispose(); - } - - Isolate* isolate_; - RandomNumberGenerator rng_; - NodeVector strong_nodes_; - NodeMap weak_nodes_; - int32_t weak_offset_; -}; - - -TEST(RandomMutation) { - v8::V8::Initialize(); - Isolate* isolate = Isolate::Current(); - CHECK_EQ(0, isolate->global_handles()->block_count()); - HandleScope handle_scope(isolate); - v8::Context::Scope context_scope( - v8::Context::New(reinterpret_cast(isolate))); - RandomMutationData data(isolate); - // grow some - data.Mutate(0.65); - data.Mutate(0.55); - // balanced mutation - for (int i = 0; i < 3; i++) data.Mutate(0.50); - // shrink some - data.Mutate(0.45); - data.Mutate(0.35); - // clear everything - data.RemoveAll(); -} - - TEST(EternalHandles) { CcTest::InitializeVM(); Isolate* isolate = Isolate::Current(); @@ -518,4 +364,3 @@ TEST(EternalHandles) { CHECK_EQ(kArrayLength, eternals->NumberOfHandles()); } - diff --git a/deps/v8/test/cctest/test-heap.cc b/deps/v8/test/cctest/test-heap.cc index 5f71350..df799bd 100644 --- a/deps/v8/test/cctest/test-heap.cc +++ b/deps/v8/test/cctest/test-heap.cc @@ -2826,6 +2826,7 @@ void ReleaseStackTraceDataTest(const char* source, const char* accessor) { // to check whether the data is being released since the external string // resource's callback is fired when the external string is GC'ed. FLAG_use_ic = false; // ICs retain objects. + FLAG_parallel_recompilation = false; CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); SourceResource* resource = new SourceResource(i::StrDup(source)); diff --git a/deps/v8/test/cctest/test-log-stack-tracer.cc b/deps/v8/test/cctest/test-log-stack-tracer.cc index 7c3567c..09df19e 100644 --- a/deps/v8/test/cctest/test-log-stack-tracer.cc +++ b/deps/v8/test/cctest/test-log-stack-tracer.cc @@ -157,7 +157,7 @@ void TraceExtension::JSTrace(const v8::FunctionCallbackInfo& args) { static Address GetJsEntrySp() { CHECK_NE(NULL, i::Isolate::Current()->thread_local_top()); - return Isolate::js_entry_sp(i::Isolate::Current()->thread_local_top()); + return i::Isolate::Current()->js_entry_sp(); } diff --git a/deps/v8/test/cctest/test-strings.cc b/deps/v8/test/cctest/test-strings.cc index d6591a0..310d93c 100644 --- a/deps/v8/test/cctest/test-strings.cc +++ b/deps/v8/test/cctest/test-strings.cc @@ -40,6 +40,58 @@ #include "cctest.h" #include "zone-inl.h" +// Adapted from http://en.wikipedia.org/wiki/Multiply-with-carry +class RandomNumberGenerator { + public: + RandomNumberGenerator() { + init(); + } + + void init(uint32_t seed = 0x5688c73e) { + static const uint32_t phi = 0x9e3779b9; + c = 362436; + i = kQSize-1; + Q[0] = seed; + Q[1] = seed + phi; + Q[2] = seed + phi + phi; + for (unsigned j = 3; j < kQSize; j++) { + Q[j] = Q[j - 3] ^ Q[j - 2] ^ phi ^ j; + } + } + + uint32_t next() { + uint64_t a = 18782; + uint32_t r = 0xfffffffe; + i = (i + 1) & (kQSize-1); + uint64_t t = a * Q[i] + c; + c = (t >> 32); + uint32_t x = static_cast(t + c); + if (x < c) { + x++; + c++; + } + return (Q[i] = r - x); + } + + uint32_t next(int max) { + return next() % max; + } + + bool next(double threshold) { + ASSERT(threshold >= 0.0 && threshold <= 1.0); + if (threshold == 1.0) return true; + if (threshold == 0.0) return false; + uint32_t value = next() % 100000; + return threshold > static_cast(value)/100000.0; + } + + private: + static const uint32_t kQSize = 4096; + uint32_t Q[kQSize]; + uint32_t c; + uint32_t i; +}; + using namespace v8::internal; diff --git a/deps/v8/test/intl/intl.status b/deps/v8/test/intl/intl.status index 7ef0abb..34610a5 100644 --- a/deps/v8/test/intl/intl.status +++ b/deps/v8/test/intl/intl.status @@ -31,3 +31,6 @@ prefix intl date-format/resolved-options: FAIL date-format/timezone: FAIL general/v8Intl-exists: FAIL + +# TODO(jochen): The following test is flaky. +overrides/caching: PASS || FAIL diff --git a/deps/v8/test/mjsunit/compiler/escape-analysis.js b/deps/v8/test/mjsunit/compiler/escape-analysis.js new file mode 100644 index 0000000..9776f67 --- /dev/null +++ b/deps/v8/test/mjsunit/compiler/escape-analysis.js @@ -0,0 +1,134 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax --use-escape-analysis + + +// Test stores on a join path. +(function testJoin() { + function constructor() { + this.a = 0; + } + function join(mode, expected) { + var object = new constructor(); + if (mode) { + object.a = 1; + } else { + object.a = 2; + } + assertEquals(expected, object.a); + } + join(true, 1); join(true, 1); + join(false, 2); join(false, 2); + %OptimizeFunctionOnNextCall(join); + join(true, 1); join(false, 2); +})(); + + +// Test loads and stores inside a loop. +(function testLoop() { + function constructor() { + this.a = 0; + this.b = 23; + } + function loop() { + var object = new constructor(); + for (var i = 1; i < 10; i++) { + object.a = object.a + i; + assertEquals(i*(i+1)/2, object.a); + assertEquals(23, object.b); + } + assertEquals(45, object.a); + assertEquals(23, object.b); + } + loop(); loop(); + %OptimizeFunctionOnNextCall(loop); + loop(); loop(); +})(); + + +// Test loads and stores inside nested loop. +(function testNested() { + function constructor() { + this.a = 0; + this.b = 0; + this.c = 23; + } + function nested() { + var object = new constructor(); + for (var i = 1; i < 10; i++) { + object.a = object.a + i; + assertEquals(i*(i+1)/2, object.a); + assertEquals((i-1)*6, object.b); + assertEquals(23, object.c); + for (var j = 1; j < 4; j++) { + object.b = object.b + j; + assertEquals(i*(i+1)/2, object.a); + assertEquals((i-1)*6+j*(j+1)/2, object.b); + assertEquals(23, object.c); + } + assertEquals(i*(i+1)/2, object.a); + assertEquals(i*6, object.b); + assertEquals(23, object.c); + } + assertEquals(45, object.a); + assertEquals(54, object.b); + assertEquals(23, object.c); + } + nested(); nested(); + %OptimizeFunctionOnNextCall(nested); + nested(); nested(); +})(); + + +// Test deoptimization with captured objects in local variables. +(function testDeoptLocal() { + var deopt = { deopt:false }; + function constructor1() { + this.a = 1.0; + this.b = 2.3; + this.c = 3.0; + } + function constructor2(o) { + this.d = o; + this.e = 4.5; + } + function func() { + var o1 = new constructor1(); + var o2 = new constructor2(o1); + deopt.deopt; + assertEquals(1.0, o1.a); + assertEquals(2.3, o2.d.b); + assertEquals(3.0, o2.d.c); + assertEquals(4.5, o2.e); + } + func(); func(); + %OptimizeFunctionOnNextCall(func); + func(); func(); + delete deopt.deopt; + func(); func(); +})(); diff --git a/deps/v8/test/mjsunit/constant-compare-nil-value.js b/deps/v8/test/mjsunit/constant-compare-nil-value.js new file mode 100644 index 0000000..9f5b2ad --- /dev/null +++ b/deps/v8/test/mjsunit/constant-compare-nil-value.js @@ -0,0 +1,42 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +function inlined() { + return 1; +} + +function foo() { + if ((inlined() + 0.5) == null) return "null"; + return "non-null"; +} + +assertEquals("non-null", foo()); +assertEquals("non-null", foo()); +%OptimizeFunctionOnNextCall(foo); +assertEquals("non-null", foo()); diff --git a/deps/v8/test/mjsunit/debug-evaluate-const.js b/deps/v8/test/mjsunit/debug-evaluate-const.js new file mode 100644 index 0000000..cb9695b --- /dev/null +++ b/deps/v8/test/mjsunit/debug-evaluate-const.js @@ -0,0 +1,121 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --expose-debug-as debug + +Debug = debug.Debug + +listenerComplete = false; +exception = false; + +// var0: init after break point, changed by debug eval. +// const0: init before break point, changed by debug eval. +// const1: init after break point, materialized but untouched by debug eval. +// const2: init after break point, materialized and changed by debug eval. +// const3: context allocated const, init before break point, changed by eval. +function f() { + var var1 = 21; + const const3 = 3; + + function g() { + const const0 = 0; + assertEquals(undefined, const1); + assertEquals(undefined, const2); + assertEquals(3, const3); + assertEquals(21, var1); + + debugger; // Break point. + + assertEquals(30, var0); + // TODO(yangguo): debug evaluate should not be able to alter + // stack-allocated const values + // assertEquals(0, const0); + assertEquals(undefined, const1); + assertEquals(undefined, const2); + var var0 = 20; + const const1 = 1; + const const2 = 2; + assertEquals(20, var0); + assertEquals(1, const1); + assertEquals(2, const2); + } + + g(); + + assertEquals(31, var1); + assertEquals(3, const3); +} + + +function listener(event, exec_state, event_data, data) { + if (event != Debug.DebugEvent.Break) return; + try { + var frame = exec_state.frame(0); + var evaluate = function(something) { + return frame.evaluate(something).value() + } + + var count = frame.localCount(); + assertEquals(4, count); + var expectation = { "const0" : 0, + "const1" : undefined, + "const2" : undefined, + "const3" : 3, + "var0" : undefined, + "var1" : 21 }; + for (var i = 0; i < frame.localCount(); ++i) { + var name = frame.localName(i); + var value = frame.localValue(i).value(); + assertEquals(expectation[name], value); + } + + evaluate('const0 = 10'); + evaluate('const2 = 12'); + evaluate('const3 = 13'); + evaluate('var0 = 30'); + evaluate('var1 = 31'); + + // Indicate that all was processed. + listenerComplete = true; + } catch (e) { + exception = e; + print("Caught something. " + e + " " + e.stack); + }; +}; + +// Run and compile before debugger is active. +try { f(); } catch (e) { } + +Debug.setListener(listener); + +f(); + +Debug.setListener(null); + +assertFalse(exception, "exception in listener") +assertTrue(listenerComplete); + diff --git a/deps/v8/test/mjsunit/debug-evaluate-locals.js b/deps/v8/test/mjsunit/debug-evaluate-locals.js index e6326e1..ba3e92d 100644 --- a/deps/v8/test/mjsunit/debug-evaluate-locals.js +++ b/deps/v8/test/mjsunit/debug-evaluate-locals.js @@ -134,7 +134,7 @@ function listener(event, exec_state, event_data, data) { listenerComplete = true; } } catch (e) { - exception = e + exception = e; print("Caught something. " + e + " " + e.stack); }; }; diff --git a/deps/v8/test/mjsunit/debug-stepin-positions.js b/deps/v8/test/mjsunit/debug-stepin-positions.js new file mode 100644 index 0000000..482e21b --- /dev/null +++ b/deps/v8/test/mjsunit/debug-stepin-positions.js @@ -0,0 +1,142 @@ +// Copyright 2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --expose-debug-as debug --nocrankshaft +// Get the Debug object exposed from the debug context global object. +Debug = debug.Debug + +function DebuggerStatement() { + debugger; +} + +function TestCase(fun) { + var exception = false; + var codeSnippet = undefined; + var resultPositions = undefined; + + function listener(event, exec_state, event_data, data) { + try { + if (event == Debug.DebugEvent.Break) { + Debug.setListener(null); + + var secondFrame = exec_state.frame(1); + codeSnippet = secondFrame.sourceLineText(); + resultPositions = secondFrame.stepInPositions(); + } + } catch (e) { + exception = e + } + } + + Debug.setListener(listener); + + fun(); + + Debug.setListener(null); + + assertTrue(!exception, exception); + + var expectedPositions = {}; + var markPattern = new RegExp("/\\*#\\*/", "g"); + + var matchResult; + while ( (matchResult = markPattern.exec(codeSnippet)) ) { + expectedPositions[matchResult.index] = true; + } + + print(codeSnippet); + + var decoratedResult = codeSnippet; + + function replaceStringRange(s, pos, substitute) { + return s.substring(0, pos) + substitute + + s.substring(pos + substitute.length); + } + + var markLength = 5; + var unexpectedPositionFound = false; + + for (var i = 0; i < resultPositions.length; i++) { + var col = resultPositions[i].position.column - markLength; + if (expectedPositions[col]) { + delete expectedPositions[col]; + decoratedResult = replaceStringRange(decoratedResult, col, "*YES*"); + } else { + decoratedResult = replaceStringRange(decoratedResult, col, "!BAD!"); + unexpectedPositionFound = true; + } + } + + print(decoratedResult); + + for (var n in expectedPositions) { + assertTrue(false, "Some positions are not reported: " + decoratedResult); + break; + } + assertFalse(unexpectedPositionFound, "Found unexpected position: " + + decoratedResult); +} + + +// Test cases. + +// Method calls. +var fun = function() { + var data = { + a: function() {} + }; + var res = [ DebuggerStatement(), data./*#*/a(), data[/*#*/String("a")]/*#*/(), data["a"]/*#*/(), data.a, data["a"] ]; +}; +TestCase(fun); + +// Function call on a value. +var fun = function() { + function g(p) { + return g; + } + var res = [ DebuggerStatement(), /*#*/g(2), /*#*/g(2)/*#*/(3), /*#*/g(0)/*#*/(0)/*#*/(g) ]; +}; +TestCase(fun); + +// Local function call, closure function call, +// local function construction call. +var fun = (function(p) { + return function() { + function f(a, b) { + } + var res = /*#*/f(DebuggerStatement(), /*#*/p(/*#*/new f())); + }; +})(Object); +TestCase(fun); + +// Global function, global object construction, calls before pause point. +var fun = (function(p) { + return function() { + var res = [ Math.abs(new Object()), DebuggerStatement(), Math./*#*/abs(4), /*#*/new Object()./*#*/toString() ]; + }; +})(Object); +TestCase(fun); diff --git a/deps/v8/test/mjsunit/harmony/object-observe.js b/deps/v8/test/mjsunit/harmony/object-observe.js index 06254ee..830eb1b 100644 --- a/deps/v8/test/mjsunit/harmony/object-observe.js +++ b/deps/v8/test/mjsunit/harmony/object-observe.js @@ -646,9 +646,8 @@ function recursiveObserver2(r) { Object.observe(obj1, recursiveObserver2); Object.observe(obj2, recursiveObserver2); ++obj1.a; -// TODO(verwaest): Disabled because of bug 2774. -// Object.deliverChangeRecords(recursiveObserver2); -// assertEquals(199, recordCount); +Object.deliverChangeRecords(recursiveObserver2); +assertEquals(199, recordCount); // Observing named properties. diff --git a/deps/v8/test/mjsunit/load_poly_effect.js b/deps/v8/test/mjsunit/load_poly_effect.js new file mode 100644 index 0000000..7663d86 --- /dev/null +++ b/deps/v8/test/mjsunit/load_poly_effect.js @@ -0,0 +1,47 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +var o = {}; +var count = 0; +Object.defineProperty(o, "x", {get:function(){count++;return 100}}); + +function load(o, o2) { + return 1 + (o.x, o2.x_tagged); +} + +var deopt = false; +var o2 = {x_tagged:{}}; +o2.x_tagged = 1; + +load({x:1}, o2); +load({x:1}, o2); +print(load(o, o2)); +%OptimizeFunctionOnNextCall(load); +o2.x_tagged = true; +print(load(o, o2)); diff --git a/deps/v8/test/mjsunit/mjsunit.status b/deps/v8/test/mjsunit/mjsunit.status index ee35af5..829efaf 100644 --- a/deps/v8/test/mjsunit/mjsunit.status +++ b/deps/v8/test/mjsunit/mjsunit.status @@ -251,5 +251,8 @@ harmony/object-observe: SKIP readonly: SKIP array-feedback: SKIP +# TODO(mstarzinger): Enable once escape analysis is stabilized. +compiler/escape-analysis: SKIP + # Deopt every n garbage collections collides with the deopt every n times flag. regress/regress-2653: SKIP diff --git a/deps/v8/test/mjsunit/object-literal-overwrite.js b/deps/v8/test/mjsunit/object-literal-overwrite.js index 5a3584d..4d19d35 100644 --- a/deps/v8/test/mjsunit/object-literal-overwrite.js +++ b/deps/v8/test/mjsunit/object-literal-overwrite.js @@ -25,6 +25,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Flags: --allow-natives-syntax + // Check that constants and computed properties are overwriting each other // correctly, i.e., the last initializer for any name is stored in the object. @@ -49,7 +51,7 @@ var foo3 = { var foo4 = { bar: function(b){}, - bar: 7, + bar: 4, bar: function(){return 7}, }; @@ -68,6 +70,14 @@ var foo7 = { 15: 7 } +function foo8(i) { + var obj = { + x: {a: i}, + x: 7 + }; + return obj.x; +}; + assertEquals(7, foo1.bar); assertEquals(7, foo2.bar); assertEquals(7, foo3.bar); @@ -76,6 +86,12 @@ assertEquals(7, foo5[13]); assertEquals(7, foo6[14.31]); assertEquals(7, foo7[15]); +assertEquals(7, foo8(1)); +assertEquals(7, foo8(1)); +%OptimizeFunctionOnNextCall(foo8); +assertEquals(7, foo8(1)); + + // Test for the classic code generator. function fun(x) { diff --git a/deps/v8/test/mjsunit/regress/json-stringifier-emptyhandle.js b/deps/v8/test/mjsunit/regress/json-stringifier-emptyhandle.js new file mode 100644 index 0000000..970b0b8 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/json-stringifier-emptyhandle.js @@ -0,0 +1,44 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +function explode() { + var array = [1,2,3]; + + Object.defineProperty(array, 4, { + get: function () { throw "dynamite"; }, + }); + + JSON.stringify(array); +} + +try { + explode(); + assertUnreachable(); +} catch(e) { + assertEquals("dynamite", e); +} diff --git a/deps/v8/test/mjsunit/regress/regress-2836.js b/deps/v8/test/mjsunit/regress/regress-2836.js new file mode 100644 index 0000000..682cc5e --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-2836.js @@ -0,0 +1,38 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +function f() { + var end = 1073741823; // 2^30 - 1 + var start = end - 100000; // Run long enough to trigger OSR. + for (var i = start; i <= end; ++i) { + assertTrue(i >= start); // No overflow allowed! + } +} + +f(); diff --git a/deps/v8/test/mjsunit/regress/regress-convert-hole2.js b/deps/v8/test/mjsunit/regress/regress-convert-hole2.js new file mode 100644 index 0000000..b434ed3 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-convert-hole2.js @@ -0,0 +1,86 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Flags: --allow-natives-syntax --notrack-allocation-sites + +// Test adding undefined from hole in double-holey to string. +var a = [1.5, , 1.8]; + +function f(a, i, l) { + var v = a[i]; + return l + v; +} + +assertEquals("test1.5", f(a, 0, "test")); +assertEquals("test1.5", f(a, 0, "test")); +%OptimizeFunctionOnNextCall(f); +assertEquals("testundefined", f(a, 1, "test")); + +// Test double-hole going through a phi to a string-add. +function f2(b, a1, a2) { + var v; + if (b) { + v = a1[0]; + } else { + v = a2[0]; + } + x = v * 2; + return "test" + v + x; +} + +f2(true, [1.4,1.8,,1.9], [1.4,1.8,,1.9]); +f2(true, [1.4,1.8,,1.9], [1.4,1.8,,1.9]); +f2(false, [1.4,1.8,,1.9], [1.4,1.8,,1.9]); +f2(false, [1.4,1.8,,1.9], [1.4,1.8,,1.9]); +%OptimizeFunctionOnNextCall(f2); +assertEquals("testundefinedNaN", f2(false, [,1.8,,1.9], [,1.9,,1.9])); + +// Test converting smi-hole to double-hole. +function t_smi(a) { + a[0] = 1.5; +} + +t_smi([1,,3]); +t_smi([1,,3]); +t_smi([1,,3]); +%OptimizeFunctionOnNextCall(t_smi); +var ta = [1,,3]; +t_smi(ta); +ta.__proto__ = [6,6,6]; +assertEquals([1.5,6,3], ta); + +// Test converting double-hole to tagged-hole. +function t(b) { + b[1] = {}; +} + +t([1.4, 1.6,,1.8, NaN]); +t([1.4, 1.6,,1.8, NaN]); +%OptimizeFunctionOnNextCall(t); +var a = [1.6, 1.8,,1.9, NaN]; +t(a); +a.__proto__ = [6,6,6,6,6]; +assertEquals([1.6, {}, 6, 1.9, NaN], a); diff --git a/deps/v8/test/mjsunit/regress/regress-crbug-272564.js b/deps/v8/test/mjsunit/regress/regress-crbug-272564.js new file mode 100644 index 0000000..5475298 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-crbug-272564.js @@ -0,0 +1,49 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +function Bb(w) { + this.width = w; +} + +function ce(a, b) { + "number" == typeof a && (a = (b ? Math.round(a) : a) + "px"); + return a +} + +function pe(a, b, c) { + if (b instanceof Bb) b = b.width; + a.width = ce(b, !0); +} + +var a = new Bb(1); +var b = new Bb(5); +pe(a, b, 0); +pe(a, b, 0); +%OptimizeFunctionOnNextCall(pe); +pe(a, b, 0); diff --git a/deps/v8/test/mjsunit/regress/regress-et-clobbers-doubles.js b/deps/v8/test/mjsunit/regress/regress-et-clobbers-doubles.js new file mode 100644 index 0000000..47fa479 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-et-clobbers-doubles.js @@ -0,0 +1,39 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax +function t_smi(a) { + a[0] = 1.5; +} + +t_smi([1,,3]); +t_smi([1,,3]); +t_smi([1,,3]); +%OptimizeFunctionOnNextCall(t_smi); +var ta = [1,,3]; +t_smi(ta); +assertEquals([1.5,,3], ta); diff --git a/deps/v8/test/mjsunit/regress/regress-freeze.js b/deps/v8/test/mjsunit/regress/regress-freeze.js new file mode 100644 index 0000000..6f3de2a --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-freeze.js @@ -0,0 +1,46 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +// CountOperation +function f(o) { o.x++ }; +var o = {x: 5}; +Object.freeze(o); +f(o); +f(o); +%OptimizeFunctionOnNextCall(f); +f(o); +assertEquals(5, o.x); + +// Compound Assignment +function f2(o) { o.x+=3 }; +f2(o); +f2(o); +%OptimizeFunctionOnNextCall(f2); +f2(o); +assertEquals(5, o.x); diff --git a/deps/v8/test/mjsunit/regress/regress-hoist-load-named-field.js b/deps/v8/test/mjsunit/regress/regress-hoist-load-named-field.js new file mode 100644 index 0000000..7df07a0 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-hoist-load-named-field.js @@ -0,0 +1,66 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +// Load fields should not be hoisted beyond their check maps when the check maps +// cannot be hoisted due to changes in elements kinds. +function f(o, a) { + var v; + var i = 1; + while (i < 2) { + v = o.y; + a[0] = 1.5; + i++; + } + return v; +} + +f({y:1.4}, [1]); +f({y:1.6}, [1]); +%OptimizeFunctionOnNextCall(f); +f({x:1}, [1]); + +// Polymorphic loads should not be hoisted beyond their compare maps. +function f2(o) { + var i = 0; + var v; + while (i < 1) { + v = o.x; + i++; + } + return v; +} + +var o1 = { x: 1.5 }; +var o2 = { y: 1, x: 1 }; + +f2(o1); +f2(o1); +f2(o2); +%OptimizeFunctionOnNextCall(f2); +f2(o2); diff --git a/deps/v8/test/mjsunit/regress/regress-map-invalidation-1.js b/deps/v8/test/mjsunit/regress/regress-map-invalidation-1.js new file mode 100644 index 0000000..bcc6bbb --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-map-invalidation-1.js @@ -0,0 +1,48 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +var c = { x: 2, y: 1 }; + +function h() { + %MigrateInstance(c); + return 2; +} +%NeverOptimizeFunction(h); + +function f() { + for (var i = 0; i < 100000; i++) { + var n = c.x + h(); + assertEquals(4, n); + } + var o2 = [{ x: 2.5, y:1 }]; + return o2; +} + +f(); + diff --git a/deps/v8/test/mjsunit/regress/regress-map-invalidation-2.js b/deps/v8/test/mjsunit/regress/regress-map-invalidation-2.js new file mode 100644 index 0000000..6605ba7 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-map-invalidation-2.js @@ -0,0 +1,49 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --allow-natives-syntax + +var c = { x: 2, y: 1 }; + +function g() { + var outer = { foo: 1 }; + function f() { + var n = outer.foo; + for (var i = 0; i < 100000; i++) { + n += c.x + outer.foo; + } + var o2 = [{ x: 1.5, y: 1 }]; + return o2; + } + return f; +} + +var fun = g(); +fun(); +assertOptimized(fun); +fun(); + diff --git a/deps/v8/src/extensions/i18n/collator.h b/deps/v8/test/mjsunit/regress/regress-phi-truncation.js similarity index 58% rename from deps/v8/src/extensions/i18n/collator.h rename to deps/v8/test/mjsunit/regress/regress-phi-truncation.js index a3991b9..940efe3 100644 --- a/deps/v8/src/extensions/i18n/collator.h +++ b/deps/v8/test/mjsunit/regress/regress-phi-truncation.js @@ -24,45 +24,66 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// limitations under the License. -#ifndef V8_EXTENSIONS_I18N_COLLATOR_H_ -#define V8_EXTENSIONS_I18N_COLLATOR_H_ +// Flags: --allow-natives-syntax -#include "unicode/uversion.h" -#include "v8.h" - -namespace U_ICU_NAMESPACE { -class Collator; -class UnicodeString; +function test(fun, expectation) { + assertEquals(1, fun(1)); + %OptimizeFunctionOnNextCall(fun); + assertEquals(expectation, fun(0)); } -namespace v8_i18n { +test(function(x) { + var a = x ? true : false; + return a | 0; +}, 0); + +test(function(x) { + var a = x ? true : true; + return a | 0; +}, 1); + +test(function(x) { + var a = x ? true : "0"; + return a | 0; +}, 0); -class Collator { - public: - static void JSCreateCollator(const v8::FunctionCallbackInfo& args); +test(function(x) { + var a = x ? true : "1"; + return a | 0; +}, 1); - // Helper methods for various bindings. +test(function(x) { + var a = x ? true : "-1"; + return a | 0; +}, -1); - // Unpacks collator object from corresponding JavaScript object. - static icu::Collator* UnpackCollator(v8::Handle obj); +test(function(x) { + var a = x ? true : "-0"; + return a | 0; +}, 0); - // Release memory we allocated for the Collator once the JS object that - // holds the pointer gets garbage collected. - static void DeleteCollator(v8::Isolate* isolate, - v8::Persistent* object, - void* param); +test(function(x) { + var a = x ? true : "0x1234"; + return a | 0; +}, 0x1234); - // Compare two strings and returns -1, 0 and 1 depending on - // whether string1 is smaller than, equal to or larger than string2. - static void JSInternalCompare( - const v8::FunctionCallbackInfo& args); +test(function(x) { + var a = x ? true : { valueOf: function() { return 2; } }; + return a | 0; +}, 2); - private: - Collator() {} -}; +test(function(x) { + var a = x ? true : undefined; + return a | 0; +}, 0); -} // namespace v8_i18n +test(function(x) { + var a = x ? true : null; + return a | 0; +}, 0); -#endif // V8_EXTENSIONS_I18N_COLLATOR_H_ +test(function(x) { + var a = x ? true : ""; + return a | 0; +}, 0); diff --git a/deps/v8/test/mjsunit/regress/regress-prepare-break-while-recompile.js b/deps/v8/test/mjsunit/regress/regress-prepare-break-while-recompile.js new file mode 100644 index 0000000..e494173 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-prepare-break-while-recompile.js @@ -0,0 +1,62 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --expose-debug-as debug --allow-natives-syntax +// Flags: --parallel-recompilation-delay=300 + +if (!%IsParallelRecompilationSupported()) { + print("Parallel recompilation is disabled. Skipping this test."); + quit(); +} + +Debug = debug.Debug + +function foo() { + var x = 1; + return x; +} + +function bar() { + var x = 2; + return x; +} + +foo(); +// Mark and trigger parallel optimization. +%OptimizeFunctionOnNextCall(foo, "parallel"); +foo(); + +// Set break points on an unrelated function. This clears both optimized +// and (shared) unoptimized code on foo, and sets both to lazy-compile builtin. +// Clear the break point immediately after to deactivate the debugger. +Debug.setBreakPoint(bar, 0, 0); +Debug.clearAllBreakPoints(); + +// Install optimized code when parallel optimization finishes. +// This needs to be able to deal with shared code being a builtin. +assertUnoptimized(foo, "sync"); + diff --git a/deps/v8/src/extensions/i18n/number-format.h b/deps/v8/test/mjsunit/regress/regress-smi-math-floor-round.js similarity index 57% rename from deps/v8/src/extensions/i18n/number-format.h rename to deps/v8/test/mjsunit/regress/regress-smi-math-floor-round.js index d4dbc4d..7c033a3 100644 --- a/deps/v8/src/extensions/i18n/number-format.h +++ b/deps/v8/test/mjsunit/regress/regress-smi-math-floor-round.js @@ -24,46 +24,44 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// limitations under the License. -#ifndef V8_EXTENSIONS_I18N_NUMBER_FORMAT_H_ -#define V8_EXTENSIONS_I18N_NUMBER_FORMAT_H_ +// Flags: --allow-natives-syntax -#include "unicode/uversion.h" -#include "v8.h" -namespace U_ICU_NAMESPACE { -class DecimalFormat; +function f(o) { + return Math.floor(o.x_smi) + 1; } -namespace v8_i18n { +assertEquals(2, f({x_smi:1})); +assertEquals(2, f({x_smi:1})); +%OptimizeFunctionOnNextCall(f); +assertEquals(2, f({x_smi:1})); -class NumberFormat { - public: - static void JSCreateNumberFormat( - const v8::FunctionCallbackInfo& args); - - // Helper methods for various bindings. - - // Unpacks date format object from corresponding JavaScript object. - static icu::DecimalFormat* UnpackNumberFormat(v8::Handle obj); - - // Release memory we allocated for the NumberFormat once the JS object that - // holds the pointer gets garbage collected. - static void DeleteNumberFormat(v8::Isolate* isolate, - v8::Persistent* object, - void* param); +function f2(o) { + return Math.floor(o.x_tagged) + 1; +} - // Formats number and returns corresponding string. - static void JSInternalFormat(const v8::FunctionCallbackInfo& args); +var o = {x_tagged:{}}; +o.x_tagged = 1.4; +assertEquals(2, f2(o)); +assertEquals(2, f2(o)); +%OptimizeFunctionOnNextCall(f2); +assertEquals(2, f2(o)); - // Parses a string and returns a number. - static void JSInternalParse(const v8::FunctionCallbackInfo& args); +function f3(o) { + return Math.round(o.x_smi) + 1; +} - private: - NumberFormat(); -}; +assertEquals(2, f3({x_smi:1})); +assertEquals(2, f3({x_smi:1})); +%OptimizeFunctionOnNextCall(f3); +assertEquals(2, f3({x_smi:1})); -} // namespace v8_i18n +function f4(o) { + return Math.round(o.x_tagged) + 1; +} -#endif // V8_EXTENSIONS_I18N_NUMBER_FORMAT_H_ +assertEquals(2, f4(o)); +assertEquals(2, f4(o)); +%OptimizeFunctionOnNextCall(f4); +assertEquals(2, f4(o)); diff --git a/deps/v8/test/webkit/webkit.status b/deps/v8/test/webkit/webkit.status index a437e93..4aaf8a9 100644 --- a/deps/v8/test/webkit/webkit.status +++ b/deps/v8/test/webkit/webkit.status @@ -33,6 +33,3 @@ sort-large-array: PASS, SKIP if $mode == debug ############################################################################## [ $deopt_fuzzer == True ] - -# Issue 2815. -fast/js/kde/encode_decode_uri: SKIP diff --git a/deps/v8/tools/gcmole/Makefile b/deps/v8/tools/gcmole/Makefile index 23c029c..764245c 100644 --- a/deps/v8/tools/gcmole/Makefile +++ b/deps/v8/tools/gcmole/Makefile @@ -40,4 +40,4 @@ libgcmole.so: gcmole.cc -shared -o libgcmole.so gcmole.cc clean: - rm libgcmole.so + rm -f libgcmole.so diff --git a/deps/v8/tools/gcmole/bootstrap.sh b/deps/v8/tools/gcmole/bootstrap.sh new file mode 100755 index 0000000..baa0b1f --- /dev/null +++ b/deps/v8/tools/gcmole/bootstrap.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash + +# Copyright 2013 the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# This script will build libgcmole.so. + +CLANG_RELEASE=2.9 + +THIS_DIR="$(dirname "${0}")" +LLVM_DIR="${THIS_DIR}/../../third_party/llvm" +CLANG_DIR="${LLVM_DIR}/tools/clang" + +LLVM_REPO_URL=${LLVM_URL:-https://llvm.org/svn/llvm-project} + +# Die if any command dies. +set -e + +OS="$(uname -s)" + +# Xcode and clang don't get along when predictive compilation is enabled. +# http://crbug.com/96315 +if [[ "${OS}" = "Darwin" ]] && xcodebuild -version | grep -q 'Xcode 3.2' ; then + XCONF=com.apple.Xcode + if [[ "${GYP_GENERATORS}" != "make" ]] && \ + [ "$(defaults read "${XCONF}" EnablePredictiveCompilation)" != "0" ]; then + echo + echo " HEARKEN!" + echo "You're using Xcode3 and you have 'Predictive Compilation' enabled." + echo "This does not work well with clang (http://crbug.com/96315)." + echo "Disable it in Preferences->Building (lower right), or run" + echo " defaults write ${XCONF} EnablePredictiveCompilation -boolean NO" + echo "while Xcode is not running." + echo + fi + + SUB_VERSION=$(xcodebuild -version | sed -Ene 's/Xcode 3\.2\.([0-9]+)/\1/p') + if [[ "${SUB_VERSION}" < 6 ]]; then + echo + echo " YOUR LD IS BUGGY!" + echo "Please upgrade Xcode to at least 3.2.6." + echo + fi +fi + +echo Getting LLVM r"${CLANG_RELEASE}" in "${LLVM_DIR}" +if ! svn co --force \ + "${LLVM_REPO_URL}/llvm/branches/release_${CLANG_RELEASE/./}" \ + "${LLVM_DIR}"; then + echo Checkout failed, retrying + rm -rf "${LLVM_DIR}" + svn co --force \ + "${LLVM_REPO_URL}/llvm/branches/release_${CLANG_RELEASE/./}" \ + "${LLVM_DIR}" +fi + +echo Getting clang r"${CLANG_RELEASE}" in "${CLANG_DIR}" +svn co --force \ + "${LLVM_REPO_URL}/cfe/branches/release_${CLANG_RELEASE/./}" \ + "${CLANG_DIR}" + +# Echo all commands +set -x + +NUM_JOBS=3 +if [[ "${OS}" = "Linux" ]]; then + NUM_JOBS="$(grep -c "^processor" /proc/cpuinfo)" +elif [ "${OS}" = "Darwin" ]; then + NUM_JOBS="$(sysctl -n hw.ncpu)" +fi + +# Build clang. +cd "${LLVM_DIR}" +if [[ ! -f ./config.status ]]; then + ../llvm/configure \ + --enable-optimized \ + --disable-threads \ + --disable-pthreads \ + --without-llvmgcc \ + --without-llvmgxx +fi + +MACOSX_DEPLOYMENT_TARGET=10.5 make -j"${NUM_JOBS}" +STRIP_FLAGS= +if [ "${OS}" = "Darwin" ]; then + # See http://crbug.com/256342 + STRIP_FLAGS=-x +fi +strip ${STRIP_FLAGS} Release/bin/clang +cd - + +# Build libgcmole.so +make -C "${THIS_DIR}" clean +make -C "${THIS_DIR}" LLVM_SRC_ROOT="${LLVM_DIR}" libgcmole.so + +set +x + +echo +echo You can now run gcmole using this command: +echo +echo CLANG_BIN=\"third_party/llvm/Release/bin\" lua tools/gcmole/gcmole.lua +echo diff --git a/deps/v8/tools/gcmole/gcmole.lua b/deps/v8/tools/gcmole/gcmole.lua index 66aff94..aa93247 100644 --- a/deps/v8/tools/gcmole/gcmole.lua +++ b/deps/v8/tools/gcmole/gcmole.lua @@ -103,7 +103,10 @@ local function MakeClangCommandLine(plugin, plugin_args, triple, arch_define) .. " -triple " .. triple .. " -D" .. arch_define .. " -DENABLE_DEBUGGER_SUPPORT" + .. " -DV8_I18N_SUPPORT" .. " -Isrc" + .. " -Ithird_party/icu/source/common" + .. " -Ithird_party/icu/source/i18n" end function InvokeClangPluginForEachFile(filenames, cfg, func) diff --git a/deps/v8/tools/gyp/v8.gyp b/deps/v8/tools/gyp/v8.gyp index cbf9484..66376c1 100644 --- a/deps/v8/tools/gyp/v8.gyp +++ b/deps/v8/tools/gyp/v8.gyp @@ -290,6 +290,7 @@ '../../src/double.h', '../../src/dtoa.cc', '../../src/dtoa.h', + '../../src/effects.h', '../../src/elements-kind.cc', '../../src/elements-kind.h', '../../src/elements.cc', @@ -826,14 +827,10 @@ '../../src/i18n.h', '../../src/extensions/i18n/break-iterator.cc', '../../src/extensions/i18n/break-iterator.h', - '../../src/extensions/i18n/collator.cc', - '../../src/extensions/i18n/collator.h', '../../src/extensions/i18n/i18n-extension.cc', '../../src/extensions/i18n/i18n-extension.h', '../../src/extensions/i18n/i18n-utils.cc', '../../src/extensions/i18n/i18n-utils.h', - '../../src/extensions/i18n/number-format.cc', - '../../src/extensions/i18n/number-format.h', ], 'dependencies': [ '<(DEPTH)/third_party/icu/icu.gyp:icui18n', diff --git a/deps/v8/tools/tickprocessor.js b/deps/v8/tools/tickprocessor.js index 967bd3c..f1a11cc 100644 --- a/deps/v8/tools/tickprocessor.js +++ b/deps/v8/tools/tickprocessor.js @@ -249,7 +249,8 @@ TickProcessor.VmStates = { GC: 1, COMPILER: 2, OTHER: 3, - EXTERNAL: 4 + EXTERNAL: 4, + IDLE: 5 }; diff --git a/deps/v8/tools/v8heapconst.py b/deps/v8/tools/v8heapconst.py index 591bf99..57a1f59 100644 --- a/deps/v8/tools/v8heapconst.py +++ b/deps/v8/tools/v8heapconst.py @@ -35,6 +35,7 @@ INSTANCE_TYPES = { 65: "CONS_STRING_TYPE", 69: "CONS_ASCII_STRING_TYPE", 67: "SLICED_STRING_TYPE", + 71: "SLICED_ASCII_STRING_TYPE", 66: "EXTERNAL_STRING_TYPE", 70: "EXTERNAL_ASCII_STRING_TYPE", 74: "EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE", @@ -108,6 +109,8 @@ INSTANCE_TYPES = { 186: "JS_TYPED_ARRAY_TYPE", 187: "JS_DATA_VIEW_TYPE", 174: "JS_PROXY_TYPE", + 188: "JS_SET_TYPE", + 189: "JS_MAP_TYPE", 190: "JS_WEAK_MAP_TYPE", 191: "JS_WEAK_SET_TYPE", 192: "JS_REGEXP_TYPE", @@ -231,7 +234,7 @@ KNOWN_OBJECTS = { ("OLD_POINTER_SPACE", 0x0e0a1): "ElementsTransitionSymbol", ("OLD_POINTER_SPACE", 0x0e0ad): "EmptySlowElementDictionary", ("OLD_POINTER_SPACE", 0x0e249): "ObservedSymbol", - ("OLD_POINTER_SPACE", 0x27585): "StringTable", + ("OLD_POINTER_SPACE", 0x274e9): "StringTable", ("OLD_DATA_SPACE", 0x08099): "EmptyDescriptorArray", ("OLD_DATA_SPACE", 0x080a1): "EmptyFixedArray", ("OLD_DATA_SPACE", 0x080a9): "NanValue", @@ -247,6 +250,6 @@ KNOWN_OBJECTS = { ("OLD_DATA_SPACE", 0x082c9): "EmptyExternalPixelArray", ("OLD_DATA_SPACE", 0x082d5): "InfinityValue", ("OLD_DATA_SPACE", 0x082e1): "MinusZeroValue", - ("CODE_SPACE", 0x0eb41): "JsConstructEntryCode", - ("CODE_SPACE", 0x177a1): "JsEntryCode", + ("CODE_SPACE", 0x10d01): "JsConstructEntryCode", + ("CODE_SPACE", 0x183c1): "JsEntryCode", } -- 2.7.4